[Estructura de datos y algoritmo] Reconocer la lista enlazada en la lista lineal y realizar la lista enlazada unidireccional

Este artículo sigue al anterior, hemos conocido el concepto de tablas lineales y hemos realizado tablas de secuencia estáticas y dinámicas. A continuación reconocemos un nuevo concepto de lista enlazada. Y realice varias operaciones de lista enlazada unidireccional. Si todavía no entiendes la tabla de secuencias, lee este artículo

(13 mensajes) [Estructura de datos y algoritmo] Realizar tablas de secuencias estáticas y dinámicas en tablas lineales_Blog de Xiaowang Xuecode-Blog de CSDN

Tabla de contenido

1. ¿Qué es una lista enlazada?

1. El concepto y la estructura de la lista enlazada

2. Clasificación de listas enlazadas

1. Unidireccional o bidireccional

2. Liderar o no liderar

3. Cíclico o no cíclico

En segundo lugar, la implementación de la lista enlazada

1. Lista enlazada acíclica unidireccional sin cabeza

2. Realización de funciones

1. Inicialice e imprima la lista enlazada

2. Tapón de cabeza y tapón de cola

3. Eliminación de cabeza y eliminación de cola

4. Búsqueda de lista enlazada individualmente

5. Insertar datos antes o después de la posición del nodo pos

6. Eliminar datos en la posición del nodo pos (eliminar nodo pos)

7. Eliminar un nodo después de la posición pos.

8. Destruye la lista enlazada

3. Código completo

1.Lista de enlaces.h

2.Lista de enlaces.c

3.prueba.c


prefacio

Conocemos el concepto de tabla lineal en la estructura de datos, deberíamos sentirnos mejor para entenderlo, porque el establecimiento de una tabla secuencial implica principalmente la estructura y la función de gestión de memoria dinámica, que es una forma similar a la matriz.

tenemos que pensar en esa pregunta

1. Para aumentar la capacidad, debe solicitar un nuevo espacio, copiar datos y liberar espacio antiguo, lo que consumirá mucho dinero.

2. La expansión de la capacidad es generalmente 2 veces la expansión y, a veces, se desperdiciará una cierta cantidad de espacio

Por lo tanto, para resolver los problemas anteriores, introdujimos el concepto de una lista enlazada en una lista lineal.


1. ¿Qué es una lista enlazada?

1. El concepto y la estructura de la lista enlazada

Concepto: La lista enlazada es una estructura de almacenamiento no secuencial y no secuencial en la estructura de almacenamiento físico. El orden lógico de los elementos de datos se realiza a través del orden de enlace de los punteros en la lista enlazada.

El diagrama es el siguiente:

Como puede verse en la figura anterior, las características de la lista enlazada son:

1. La estructura de la cadena es lógicamente continua, pero no necesariamente físicamente continua. (La dirección de cada nodo no es necesariamente)

2. En realidad, los nodos generalmente se aplican desde el montón.

3. El espacio que se aplica desde el montón se asigna de acuerdo con una determinada estrategia, que varía según los diferentes editores. El espacio que se aplica de nuevo puede ser continuo o discontinuo.

2. Clasificación de listas enlazadas

De hecho, hay muchas estructuras de listas enlazadas, y las siguientes combinaciones tienen 8 estructuras principales de listas enlazadas.

1. Unidireccional o bidireccional

2. Liderar o no liderar

Si se utiliza el nodo principal, no hay necesidad de asignar un valor a su campo de datos, y solo sirve como punto base para establecer una lista enlazada.Si no se utiliza, el primer nodo puede almacenar el valor, y un Se crea un nuevo nodo para el primer nodo. Simplemente haga clic en phead, de modo que la lista vinculada de nodos principales se convierta en una lista vinculada de nodos no principales.     

Lo principal es ver si el primer nodo usa su campo de valor

Usar el primer nodo del campo de datos del nodo nodo principal

   nodo principal inútil                           

3. Cíclico o no cíclico

 

Para los tipos de situaciones anteriores, hay dos de uso común, lista enlazada no circular unidireccional sin encabezado y lista enlazada circular bidireccional con encabezado

como muestra la imagen

1. Lista enlazada no circular unidireccional sin cabeza: la estructura es simple y, por lo general, no se usa solo para almacenar datos. En la práctica, es más una subestructura de otras estructuras de datos, como cubos hash, listas de adyacencia de gráficos, etc. Además, esta estructura aparece mucho en las entrevistas de prueba escritas.
2. Lista enlazada circular bidireccional líder: la estructura más compleja, generalmente utilizada para almacenar datos por separado. La estructura de datos de la lista enlazada utilizada en la práctica es una lista enlazada circular bidireccional con el líder. Además, aunque la estructura es compleja, después de usar el código para implementarla, encontrará que la estructura traerá muchas ventajas y la implementación es simple. Lo sabremos cuando implementemos el código más adelante.

En segundo lugar, la implementación de la lista enlazada

1. Lista enlazada acíclica unidireccional sin cabeza

La estructura es:

typedef int SLDataType;
//单向链表的实现、
typedef struct ListNode {
	SLDataType data;//数据域
	struct ListNode* next;//指针域
}List;

La función a implementar es:

//打印单链表
void ListPrint(List* ps);
//单链表的尾插
void ListPushBack(List** ps, SLDataType data);
//单链表的头插
void ListPushFront(List** ps, SLDataType data);
//单链表的尾删
void ListPopBack(List** ps);
//单链表的头删
void ListPopFront(List** ps);
//单链表的查找
List* ListFind(List* ps);

//在pos位置上插入数据
void ListInsertBefore(List** ps, SLDataType x, List* pos);

//在pos位置之后插入数据
void ListInsertAfter(List** ps, SLDataType x, List* pos);

//在pos位子删除数据
void ListErase(List** ps, List* pos);

//在pos位置之后一位删除数据
void ListEraseAfter(List* pos);
//单链表的摧毁
void ListDestory(List** ps);

2. Realización de funciones

1. Inicialice e imprima la lista enlazada

//初始化链表
void InitList(List* ps) {
	ps->data = 0;
	ps->next = NULL;
}
//打印单链表
void ListPrint(List* ps) {
	List* cur = ps;
	while ((cur) != NULL) {
		printf("%d -> ", cur->data);
		cur = cur->next;
	}
	printf("NULL\n");
}

2. Tapón de cabeza y tapón de cola

El diagrama de inserción de la cola es el siguiente:

el código se muestra a continuación:

//创建一个新节点
List* CreateNode(SLDataType x) {
	List* newNode = (List*)malloc(sizeof(List));
	if (newNode == NULL) {
		perror("malloc fail\n");
		exit(-1);
	}
	else {
		newNode->data = x;
		newNode->next = NULL;
	}
	return newNode;
}
//单链表的尾插
void ListPushBack(List** ps, SLDataType data) {
	//创建新的节点
	assert(ps);//断言
	List* newNode = CreateNode(data);
	if (*ps == NULL) {
		//说明是空链表
		*ps = newNode;
	}
	else {
		List* tail = *ps;
		while (tail->next != NULL) {
			tail = tail->next;
		}
		tail->next = newNode;
	}
}

Inserte el encabezado como se muestra:

el código se muestra a continuación:

//单链表的头插
void ListPushFront(List** ps, SLDataType data) {
	//先断言是否为空
	assert(ps);
	//将新地址指向头结点下一个next结点的地址,然后在用头结点指向新节点
	List* newNode = CreateNode(data);
	newNode->next = (*ps);  //new指向ps当前的位置,然后new是第一个位置了,将new赋值给ps,这样new就作为头部连接链表了
	(*ps) = newNode;//原本ps位置的数值不变,这样的话就成 new->next=ps,new数值在前,ps的数值在后

}

3. Eliminación de cabeza y eliminación de cola

La eliminación de la cola se muestra en la figura:

 demostración de código:

//单链表的尾删
void ListPopBack(List** ps) {
	assert(ps);//断言
	//三种情况
	//1.空链表
	//2.一个节点
	//3.多个节点
	if (*ps == NULL) {
		return;
	}
	//只有一个节点的情况为
	else if ((*ps)->next == NULL) {
		free(*ps); //如果只有一个头节点的话
		*ps = NULL;
	}
	else {
		//多个节点的情况下、
		List* tail = *ps;
		while (tail->next->next!= NULL) {
			tail = tail->next;
		}
		free(tail->next);
		tail->next= NULL;
	}
}

El encabezado se elimina como se muestra en la figura:

 el código se muestra a continuación:

//单链表的头删
void ListPopFront(List** ps) {
	assert(ps);
	//1.空
	//2.非空
	if (*ps == NULL) {
		//为空
		return;
	}
	else {
		List* tail = (*ps)->next;//创建临时变量tail,将头节点之后的地址给tail
		free(*ps);//滞空头节点
		*ps = NULL;//可有可不有,接下来也要用
		*ps = tail;//将tail也就是ps的下一个List节点给ps

	}
}

4. Búsqueda de lista enlazada individualmente

el código se muestra a continuación:

//单链表的查找

List* ListFind(List* ps,SLDataType data) {
	//进行查找就是进行判断是否为空链表,为空直接返回
	if (ps == NULL) {
		printf("链表为空、无法查找\n");
		return;
	}
	List* tail = ps;
	while (tail != NULL) {//从头节点开始,进行循环,
		if (tail->data == data) {
			return tail;
		}
		tail = tail->next;
	}
	return tail;//最后还找不到data,tail就为NULL了
}

5. Insertar datos antes o después de la posición del nodo pos

Inserte datos antes de la posición del nodo pos, como se muestra en la figura:

el código se muestra a continuación:

//在pos位置上插入数据
void ListInsertBefore(List** ps, SLDataType x, List* pos) {
	//先判断是否为空
	assert(ps);
	assert(pos);
	//空链表排除
	//1.pos是第一个节点
	//2.pos不是第一个节点
	if (*ps == pos) {
		//是第一个节点,那就直接头插
		ListPushFront(ps, x);
	}
	else {
		List* prev = *ps;
		while (prev->next != pos) {
			prev = prev->next;
		}
		List* newnode = CreateNode(x);
		prev->next = newnode;
		newnode->next = pos;
	}
}

Inserte un nodo después de la posición del nodo pos, como se muestra en la figura:

 el código se muestra a continuación:
 

//在pos位置之后插入数据
void ListInsertAfter(List** ps, SLDataType x, List* pos) {
	assert(ps);
	//assert(pos);//断言
	List* newnode = CreateNode(x);
	newnode->next = pos->next;
	pos->next = newnode;
}

6. Eliminar datos en la posición del nodo pos (eliminar nodo pos)

como muestra la imagen:

el código se muestra a continuación:

//在pos位子删除数据
void ListErase(List** ps, List* pos) {
	assert(ps);
	assert(pos);

	if (*ps == pos) {
		ListPopFront(ps);
	}
	else {
		List* next = *ps;
		while (next->next != pos) {
			next = next->next;
		}
		//这个时候next->next == pos
		next->next = next->next->next;
		/*free(next->next);*/
		free(pos);
		pos = NULL;
	}
}

7. Eliminar un nodo después de la posición pos.

como muestra la imagen:

el código se muestra a continuación:

//在pos位置之后一位删除数据
void ListEraseAfter(List* pos) {
	assert(pos);
	List* next = pos->next;//将pos 的下一个结点赋值给next
	if (next != NULL) {
		pos->next = pos->next->next;//表示pos的下一个的下一个结点的地址赋值给pos的指针域  实质上是将pos的下一个结点给跳过
		free(next);  //将pos的下一个结点给free释放
		next = NULL;  //next指向为NULL  防止野指针
	}
}

8. Destruye la lista enlazada

el código se muestra a continuación:

//链表的摧毁  直接将头指针指针域指向NULL
void ListDestory(List** ps) {
	//assert(ps);  //防止空链表
	一个结点一个结点释放
	//List* next = *ps;
	//while (next) {
	//	List* cur = next->next;
	//	free(next);
	//	next = cur;
	//}
	*ps = NULL;
}

Debido a que es un puntero secundario, puede directamente *ps=NULL, o liberar uno por uno

3. Código completo

1.Lista de enlaces.h

#define _CRT_SECURE_NO_WARNINGS

#include<stdio.h>
#include<stdlib.h>
#include<malloc.h>
#include<assert.h>
typedef int SLDataType;
//单向链表的实现、
typedef struct ListNode {
	SLDataType data;//数据域
	struct ListNode* next;//指针域
}List;

//打印单链表
void ListPrint(List* ps);
//单链表的尾插
void ListPushBack(List** ps, SLDataType data);
//单链表的头插
void ListPushFront(List** ps, SLDataType data);
//单链表的尾删
void ListPopBack(List** ps);
//单链表的头删
void ListPopFront(List** ps);
//单链表的查找
List* ListFind(List* ps);

//在pos位置上插入数据
void ListInsertBefore(List** ps, SLDataType x, List* pos);

//在pos位置之后插入数据
void ListInsertAfter(List** ps, SLDataType x, List* pos);

//在pos位子删除数据
void ListErase(List** ps, List* pos);

//在pos位置之后一位删除数据
void ListEraseAfter(List* pos);
//单链表的摧毁
void ListDestory(List** ps);

2.Lista de enlaces.c

#define _CRT_SECURE_NO_WARNINGS
#include"单向链表.h"

//链表的使用,在插入上面
// 如果是尾部插入,如果是空链表直接将新节点给ps 是先找到链表尾部,然后创建新节点,连接即可
// 如果是头部插入,先进行断言判空,之后创建新节点,将新节点的数据
// new->next=ps  这个是找到对应的位置,连接起来
// ps=new;  将新节点的信息传递给ps,这样ps还是头节点
// 
// 
// 
// 
// 


//进行单链表的实现

//初始化链表
void InitList(List* ps) {
	ps->data = 0;
	ps->next = NULL;
}
//打印单链表
void ListPrint(List* ps) {
	List* cur = ps;
	while ((cur) != NULL) {
		printf("%d -> ", cur->data);
		cur = cur->next;
	}
	printf("NULL\n");
}

//创建一个新节点
List* CreateNode(SLDataType x) {
	List* newNode = (List*)malloc(sizeof(List));
	if (newNode == NULL) {
		perror("malloc fail\n");
		exit(-1);
	}
	else {
		newNode->data = x;
		newNode->next = NULL;
	}
	return newNode;
}
//单链表的尾插
void ListPushBack(List** ps, SLDataType data) {
	//创建新的节点
	assert(ps);//断言
	List* newNode = CreateNode(data);
	if (*ps == NULL) {
		//说明是空链表
		*ps = newNode;
	}
	else {
		List* tail = *ps;
		while (tail->next != NULL) {
			tail = tail->next;
		}
		tail->next = newNode;
	}
}
//单链表的头插
void ListPushFront(List** ps, SLDataType data) {
	//先断言是否为空
	assert(ps);
	//将新地址指向头结点下一个next结点的地址,然后在用头结点指向新节点
	List* newNode = CreateNode(data);
	newNode->next = (*ps);  //new指向ps当前的位置,然后new是第一个位置了,将new赋值给ps,这样new就作为头部连接链表了
	(*ps) = newNode;//原本ps位置的数值不变,这样的话就成 new->next=ps,new数值在前,ps的数值在后

}

//单链表的尾删
void ListPopBack(List** ps) {
	assert(ps);//断言
	//三种情况
	//1.空链表
	//2.一个节点
	//3.多个节点
	if (*ps == NULL) {
		return;
	}
	//只有一个节点的情况为
	else if ((*ps)->next == NULL) {
		free(*ps); //如果只有一个头节点的话
		*ps = NULL;
	}
	else {
		//多个节点的情况下、
		List* tail = *ps;
		while (tail->next->next!= NULL) {
			tail = tail->next;
		}
		free(tail->next);
		tail->next= NULL;
	}
}


//单链表的头删
void ListPopFront(List** ps) {
	assert(ps);
	//1.空
	//2.非空
	if (*ps == NULL) {
		//为空
		return;
	}
	else {
		List* tail = (*ps)->next;//创建临时变量tail,将头节点之后的地址给tail
		free(*ps);//滞空头节点
		*ps = NULL;//可有可不有,接下来也要用
		*ps = tail;//将tail也就是ps的下一个List节点给ps

	}
}

//单链表的查找

List* ListFind(List* ps,SLDataType data) {
	//进行查找就是进行判断是否为空链表,为空直接返回
	if (ps == NULL) {
		printf("链表为空、无法查找\n");
		return;
	}
	List* tail = ps;
	while (tail != NULL) {//从头节点开始,进行循环,
		if (tail->data == data) {
			return tail;
		}
		tail = tail->next;
	}
	return tail;
}

//在pos位置上插入数据
void ListInsertBefore(List** ps, SLDataType x, List* pos) {
	//先判断是否为空
	assert(ps);
	assert(pos);
	//空链表排除
	//1.pos是第一个节点
	//2.pos不是第一个节点
	if (*ps == pos) {
		//是第一个节点,那就直接头插
		ListPushFront(ps, x);
	}
	else {
		List* prev = *ps;
		while (prev->next != pos) {
			prev = prev->next;
		}
		List* newnode = CreateNode(x);
		prev->next = newnode;
		newnode->next = pos;
	}
}
//在pos位置之后插入数据
void ListInsertAfter(List** ps, SLDataType x, List* pos) {
	assert(ps);
	//assert(pos);//断言
	List* newnode = CreateNode(x);
	newnode->next = pos->next;
	pos->next = newnode;
}
//在pos位子删除数据
void ListErase(List** ps, List* pos) {
	assert(ps);
	assert(pos);

	if (*ps == pos) {
		ListPopFront(ps);
	}
	else {
		List* next = *ps;
		while (next->next != pos) {
			next = next->next;
		}
		//这个时候next->next == pos
		next->next = next->next->next;
		/*free(next->next);*/
		free(pos);
		pos = NULL;
	}
}
//在pos位置之后一位删除数据
void ListEraseAfter(List* pos) {
	assert(pos);
	List* next = pos->next;//将pos 的下一个结点赋值给next
	if (next != NULL) {
		pos->next = pos->next->next;//表示pos的下一个的下一个结点的地址赋值给pos的指针域  实质上是将pos的下一个结点给跳过
		free(next);  //将pos的下一个结点给free释放
		next = NULL;  //next指向为NULL  防止野指针
	}
}
//链表的摧毁  直接将头指针指针域指向NULL
void ListDestory(List** ps) {
	//assert(ps);  //防止空链表
	一个结点一个结点释放
	//List* next = *ps;
	//while (next) {
	//	List* cur = next->next;
	//	free(next);
	//	next = cur;
	//}
	*ps = NULL;
}

3.prueba.c

#define _CRT_SECURE_NO_WARNINGS

#include"单向链表.h"
void test()
{
	List* phead=NULL;//作为头节点

	//单链表的尾插
	ListPushBack(&phead, 1);
	ListPushBack(&phead, 2);
	ListPushBack(&phead, 3);
	ListPushBack(&phead, 4);
	ListPushBack(&phead, 5);
	ListPrint(phead);

	ListPushFront(&phead, 1);
	ListPrint(phead);

	ListPopBack(&phead);
	ListPrint(phead);
	
	ListPopFront(&phead);
	ListPrint(phead);
	ListErase(&phead, phead->next);
	ListInsertAfter(&phead, 10, phead->next);
	ListEraseAfter(phead->next);

	ListPrint(phead);
	ListDestory(&phead);
}
int main()
{
	test();
	return 0;
}

Resumir

Este artículo explica principalmente cuáles son las clasificaciones de las listas enlazadas. Hay dos tipos de uso común, lista enlazada no circular unidireccional sin encabezado y lista enlazada circular bidireccional con encabezado. Hemos implementado una lista enlazada no circular unidireccional sin encabezado, que es una lista enlazada relativamente simple. Para la implementación, usamos un puntero de segundo nivel para pasar parámetros, por supuesto, también es posible usar un puntero de primer nivel para pasar parámetros. Las principales funciones de implementación son de cabeza a cola la inserción, la eliminación de la cabeza a la cola y la adición o eliminación de elementos antes y después de pos especifica la posición del nodo.

A continuación, le presentaremos otra forma de la lista enlazada más utilizada a continuación, tomando la delantera en la lista enlazada circular bidireccional.

Supongo que te gusta

Origin blog.csdn.net/qq_63319459/article/details/128766484
Recomendado
Clasificación