[Estructura de datos y algoritmo] Realice la lista enlazada circular bidireccional principal (la lista enlazada más compleja)

En el artículo anterior, nos dimos cuenta de la estructura de la lista enlazada, y nos dimos cuenta de la lista enlazada no circular unidireccional sin cabeza. A continuación, implementamos otra estructura de lista enlazada de uso común, la lista enlazada circular bidireccional principal. Si aún no comprende la lista vinculada unidireccional, lea este artículo (7 mensajes) [Estructura de datos y algoritmo] Conozca la lista vinculada en la lista lineal y realice la lista vinculada unidireccional_Xiaowang Xuecode Blog-CSDN Blog

Tabla de contenido

prefacio

1. ¿Cuál es la principal lista enlazada circular bidireccional?

2. Realice la lista enlazada circular bidireccional principal

1. La estructura y la función a implementar

2. Inicialice e imprima la lista enlazada

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

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

5. Encuentra y devuelve el número de nodos

6. Inserte un nodo antes de la posición pos.

7. Eliminar el nodo pos especificado

8. Destruye la lista enlazada

3. Código completo

1.DSLinkList.h

2.DSLinkList.c

3.prueba.c

Resumir


prefacio

La lista enlazada circular bidireccional principal es la estructura más compleja de la lista enlazada. Las funciones principales que implementamos son: insertar la cabeza e insertar la cola, eliminar la cabeza y eliminar la cola, inicialización, impresión, especificación de la posición pos para insertar o eliminar nodos y encontrar nodos, Destruir lista vinculada y otras funciones.


1. ¿Cuál es la principal lista enlazada circular bidireccional?

como muestra la imagen:


2. Realice la lista enlazada circular bidireccional principal

1. La estructura y la función a implementar

La estructura es la siguiente:

typedef struct DSLDataType;
//定义双向链表的结构体

//双向链表每一个节点都有一个前驱节点和后继节点,可以根据节点获得其前后的节点
typedef struct DSListNode {
	struct DSListNode* prev;//前驱节点
	struct DSListNode* next;//后继节点
	int value;//数据域
}DSLNode;//重定义

En comparación con la lista enlazada unidireccional, hay un campo de puntero más anterior, que apunta a la dirección del nodo anterior

La función que implementa la función:

//初始化
DSLNode*ListInit();
//打印链表
void ListPrint(DSLNode* ps);
//尾部插入
void ListPushBack(DSLNode* ps, int data);
//头部插入
void ListPushFront(DSLNode* ps, int data);
//尾部删除
void ListPopBack(DSLNode* ps);
//头部删除
void ListPopFront(DSLNode* ps);
//判空
bool ListEmpty(DSLNode* ps);
//返回链表节点个数
int Listsize(DSLNode* ps);
//寻找指定元素,返回对应的节点
DSLNode* ListFind(DSLNode* ps, int data);
//在pos之前插入节点
void ListInsert(DSLNode* pos, int data);
//删除pos位置结点
void ListEarse(DSLNode* pos);
//摧毁链表
void ListDestory(DSLNode* ps);

2. Inicialice e imprima la lista enlazada

//对于函数的实现
//初始化
DSLNode* ListInit() {
	//初始化创建链表
	//创建新节点
	DSLNode* num = (DSLNode*)malloc(sizeof(DSLNode));
	//判断是否创建成功
	if (num == NULL) {
		perror("malloc fail\n");
		exit(-1);
	}
	num->prev = num;
	num->next = num;//作为头节点,前驱和后驱结点都指向自己
	return num;
}

//打印链表
void ListPrint(DSLNode* ps) {
	assert(ps);//断言
	DSLNode* cur = ps->next;
	printf("phead->");
	while (cur != ps) {//双向链表,回到ps,就表示转了一圈
		printf("%d->", cur->value);
		cur = cur->next;
	}
	printf("NULL\n");
}

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

El tapón trasero se muestra en la figura:

el código se muestra a continuación:


DSLNode* CreatNode(int data) {//创建新节点
	DSLNode* cur = (DSLNode*)malloc(sizeof(DSLNode));
	if (cur == NULL) {
		perror("malloc fail\n");
		exit(-1);
	}
	cur->next = cur->prev = NULL;
	cur->value = data;
}
//尾部插入
void ListPushBack(DSLNode* ps, int data) {
	assert(ps);//断言
	DSLNode* newnode = CreatNode(data);
	DSLNode* tail = ps->prev;//把头节点之前的那个地址给tail
	tail->next = newnode;//将ps之前的那个数值,实际上是这个双向链表的最后一个元素的位置,的next指针指向新节点
	newnode->prev = tail;//新节点前后为 tail和ps
	newnode->next = ps;
	ps->prev = newnode;//tail的两个指针域都指向完成,就只剩下ps的前驱指针

}

El tapón de la cabeza se muestra en la figura:
 el código es el siguiente:


//头部插入
void ListPushFront(DSLNode* ps, int data) {
	assert(ps);
	//头部插入就是将ps之后的一个地址给临时变量tail
	DSLNode* tail = ps->next;
	DSLNode* newnode = CreatNode(data);//创建新节点
	ps->next->prev = newnode;
	newnode->next = ps->next;
    newnode->prev = ps;
    ps->next = newnode;
}

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

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

el código se muestra a continuación:

//判空
bool ListEmpty(DSLNode* ps) {
	assert(ps);//断言
	return ps->next == ps;//如果是只有一个ps头节点,则表示为空,返回true,否则返回false

}
//尾部删除
void ListPopBack(DSLNode* ps) {

	assert(ps);//断言
	assert(!ListEmpty(ps));//先判断是否为空
	DSLNode* cur = ps->prev;
	//将cur的next值为NULL
	ps->prev = ps->prev->prev;
	ps->prev->next = ps;
	//这是直接略过尾部最后一个元素
	//跳过ps之前的一个节点,先让其左右节点相连,然后释放这个地址
	free(cur);
	cur = NULL;
}

 El encabezado se elimina como se muestra en la figura:

el código se muestra a continuación:

//头部删除
void ListPopFront(DSLNode* ps) {
	assert(ps);
	assert(!ListEmpty(ps));
	DSLNode* cur = ps->next;
	//头删  是将头节点之后的第一个节点删除释放空间
	ps->next = ps->next->next;
	ps->next->prev = ps;
	free(cur);
	cur = NULL;
}

5. Encuentra y devuelve el número de nodos

Buscar: devuelva un nodo, de acuerdo con el campo de valor de datos, devuelva el primer nodo encontrado cur, si no devuelve NULL

//返回链表节点个数
int Listsize(DSLNode* ps) {
	assert(ps);
	int count = 0;
	DSLNode* cur = ps->next;
	while (cur != ps) {
		count++;
		cur = cur->next;
	}
	return count;
}
//查找
DSLNode* ListFind(DSLNode* ps, int data) {
	assert(ps);
	DSLNode* cur = ps->next;
	while (cur != ps) {
		if (cur->value == data) {
			return cur;
		}
	}
	return NULL;//NULL表示不存在
}

6. Inserte un nodo antes de la posición pos.

como muestra la imagen:

 el código se muestra a continuación:

//插入节点
void ListInsert(DSLNode* pos, int data) {

	assert(pos);
	//pos三种可能
	//1.空链表
	//2.只有一个节点
	DSLNode* cur = pos->prev;
	//双向链表可以直接找到pos之前的位置,单向链表只能进行循环
	DSLNode* newnode = CreatNode(data);
	pos->prev = newnode;//把新节点newnode的位置给pos的prev
	newnode->prev = cur;//cur表示的是创建new节点之前的时候pos之前的结点
	cur->next = newnode;
	newnode->next = pos;
	//另一种不使用cur的方法
	//newnode->next = pos;
	//pos->prev->next = newnode;
	//newnode->prev = pos->prev;
	//pos->prev = newnode;
}

7. Eliminar el nodo pos especificado

como muestra la imagen:

el código se muestra a continuación:


//删除指针
void ListEarse(DSLNode* pos) {
	assert(pos);
	DSLNode* cur = pos->prev;
	DSLNode* tail = pos->next;
	cur->next = tail;//两者相互指向,最后释放pos空间
	tail->prev = cur;
	free(pos);
	pos = NULL;
}

8. Destruye la lista enlazada

De dos maneras, puede usar el puntero secundario directamente, o puede usar el puntero principal uno por uno gratis y NULL

//摧毁链表
void ListDestory(DSLNode* ps) {
	//可以设计二级指针直接将ps地址滞空为NULL
	//这里还是使用一级指针
	assert(ps);
	DSLNode* cur = ps->next;
	while (cur != ps) {
		DSLNode* tail = cur->next;//这个地方就是一个精彩的地方
		free(cur);//使用临时变量tail得到cur的下一个地址,然后再free cur
		cur = tail;//tail这个时候就是cur 的下一个结点
	}
	free(ps);
	ps = NULL;

}
void ListDestory2(DSLNode** ps) {
	assert(ps);
	free(ps);//二级指针直接free
	*ps = NULL;
}

3. Código completo

1.DSLinkList.h

#define _CRT_SECURE_NO_WARNINGS

#include<stdio.h>
#include<stdlib.h>
#include<malloc.h>
#include<assert.h>
#include<stdbool.h>

typedef struct DSLDataType;
//定义双向链表的结构体

//双向链表每一个节点都有一个前驱节点和后继节点,可以根据节点获得其前后的节点
typedef struct DSListNode {
	struct DSListNode* prev;//前驱节点
	struct DSListNode* next;//后继节点
	int value;//数据域
}DSLNode;//重定义

//创建头节点,并将tail和head都指向第一个节点
struct DSList {
	struct DSListNode* tail;
	struct DSListNode* head;
	unsigned int len;//表示链表的长度
};
//初始化
DSLNode*ListInit();
//打印链表
void ListPrint(DSLNode* ps);
//尾部插入
void ListPushBack(DSLNode* ps, int data);
//头部插入
void ListPushFront(DSLNode* ps, int data);
//尾部删除
void ListPopBack(DSLNode* ps);
//头部删除
void ListPopFront(DSLNode* ps);
//判空
bool ListEmpty(DSLNode* ps);
//返回链表节点个数
int Listsize(DSLNode* ps);
//寻找指定元素,返回对应的节点
DSLNode* ListFind(DSLNode* ps, int data);
//在pos之前插入节点
void ListInsert(DSLNode* pos, int data);
//删除pos位置结点
void ListEarse(DSLNode* pos);
//摧毁链表
void ListDestory(DSLNode* ps);

2.DSLinkList.c

#define _CRT_SECURE_NO_WARNINGS

#include"DSLinkList.h"

//对于函数的实现
//初始化
DSLNode* ListInit() {
	//初始化创建链表
	//创建新节点
	DSLNode* num = (DSLNode*)malloc(sizeof(DSLNode));
	//判断是否创建成功
	if (num == NULL) {
		perror("malloc fail\n");
		exit(-1);
	}
	num->prev = num;
	num->next = num;
	return num;
}

//打印链表
void ListPrint(DSLNode* ps) {
	assert(ps);//断言
	DSLNode* cur = ps->next;
	printf("phead->");
	while (cur != ps) {//双向链表,回到ps,就表示转了一圈
		printf("%d->", cur->value);
		cur = cur->next;
	}
	printf("NULL\n");
}

DSLNode* CreatNode(int data) {//创建新节点
	DSLNode* cur = (DSLNode*)malloc(sizeof(DSLNode));
	if (cur == NULL) {
		perror("malloc fail\n");
		exit(-1);
	}
	cur->next = cur->prev = NULL;
	cur->value = data;
}
//尾部插入
void ListPushBack(DSLNode* ps, int data) {
	assert(ps);//断言
	DSLNode* newnode = CreatNode(data);
	DSLNode* tail = ps->prev;//把头节点之前的那个地址给tail
	tail->next = newnode;//将ps之前的那个数值,实际上是这个双向链表的最后一个元素的位置,的next指针指向新节点
	newnode->prev = tail;//新节点前后为 tail和ps
	newnode->next = ps;
	ps->prev = newnode;//tail的两个指针域都指向完成,就只剩下ps的前驱指针

}

//头部插入
void ListPushFront(DSLNode* ps, int data) {
	assert(ps);
	//头部插入就是将ps之后的一个地址给临时变量tail
	DSLNode* tail = ps->next;
	DSLNode* newnode = CreatNode(data);//创建新节点
	ps->next->prev = newnode;
	newnode->next = ps->next;
    newnode->prev = ps;
    ps->next = newnode;
}

//判空
bool ListEmpty(DSLNode* ps) {
	assert(ps);//断言
	return ps->next == ps;//如果是只有一个ps头节点,则表示为空,返回true,否则返回false

}

//返回链表节点个数
int Listsize(DSLNode* ps) {
	assert(ps);
	int count = 0;
	DSLNode* cur = ps->next;
	while (cur != ps) {
		count++;
		cur = cur->next;
	}
	printf("\n");
	return count;
}
//尾部删除
void ListPopBack(DSLNode* ps) {

	assert(ps);//断言
	assert(!ListEmpty(ps));//先判断是否为空
	DSLNode* cur = ps->prev;
	//将cur的next值为NULL
	ps->prev = ps->prev->prev;
	ps->prev->next = ps;
	//这是直接略过尾部最后一个元素
	//跳过ps之前的一个节点,先让其左右节点相连,然后释放这个地址
	free(cur);
	cur = NULL;
}
//头部删除
void ListPopFront(DSLNode* ps) {
	assert(ps);
	assert(!ListEmpty(ps));
	DSLNode* cur = ps->next;
	//头删  是将头节点之后的第一个节点删除释放空间
	ps->next = ps->next->next;
	ps->next->prev = ps;
	free(cur);
	cur = NULL;
}
//查找
DSLNode* ListFind(DSLNode* ps, int data) {
	assert(ps);
	DSLNode* cur = ps->next;
	while (cur != ps) {
		if (cur->value == data) {
			return cur;
		}
	}
	return NULL;//NULL表示不存在
}
//插入节点
void ListInsert(DSLNode* pos, int data) {

	assert(pos);
	//pos三种可能
	//1.空链表
	//2.只有一个节点
	DSLNode* cur = pos->prev;
	//双向链表可以直接找到pos之前的位置,单向链表只能进行循环
	DSLNode* newnode = CreatNode(data);
	pos->prev = newnode;//把新节点newnode的位置给pos的prev
	newnode->prev = cur;//cur表示的是创建new节点之前的时候pos之前的结点
	cur->next = newnode;
	newnode->next = pos;
	//另一种不使用cur的方法
	//newnode->next = pos;
	//pos->prev->next = newnode;
	//newnode->prev = pos->prev;
	//pos->prev = newnode;
}


//删除指针
void ListEarse(DSLNode* pos) {
	assert(pos);
	DSLNode* cur = pos->prev;
	DSLNode* tail = pos->next;
	cur->next = tail;//两者相互指向,最后释放pos空间
	tail->prev = cur;
	free(pos);
	pos = NULL;
}
//摧毁链表
void ListDestory(DSLNode* ps) {
	//可以设计二级指针直接将ps地址滞空为NULL
	//这里还是使用一级指针
	assert(ps);
	DSLNode* cur = ps->next;
	while (cur != ps) {
		DSLNode* tail = cur->next;
		free(cur);
		cur = tail;
	}
	free(ps);
	ps = NULL;

}
void ListDestory2(DSLNode** ps) {
	assert(ps);
	free(ps);
	*ps = NULL;
}

3.prueba.c

#define _CRT_SECURE_NO_WARNINGS
#include"DSLinkList.h"

void test()
{
	DSLNode* phead = ListInit();//初始化
	
	ListPushBack(phead, 1);
	ListPushBack(phead, 2);
	ListPushBack(phead, 3);
	ListPushBack(phead, 4);
	ListPushBack(phead, 5);//检验尾插
	ListPrint(phead);//打印

	ListPushFront(phead, 10);
	ListPushFront(phead, 20);
	ListPushFront(phead, 30);
	ListPushFront(phead, 40);
	ListPushFront(phead, 50);//检验头插
	ListPrint(phead);//打印
	printf("%d\n", Listsize(phead));//检验返回结点个数

	ListPopBack(phead);
	ListPopBack(phead);
	ListPopBack(phead);
	ListPopBack(phead);
	ListPopBack(phead);//尾删
	ListPopFront(phead);
	ListPopFront(phead);
	ListPopFront(phead);
	ListPopFront(phead);
	ListPopFront(phead);//头删
	ListPrint(phead);//打印
	ListInsert(phead->next, 10);//pos指定结点之前插入newnode新节点
	ListPrint(phead);//打印
}
int main()
{
	test();
	return 0;
}

Resumir

Este artículo explica principalmente el uso y la implementación del código de la lista enlazada circular bidireccional líder más compleja en la lista enlazada, apuntar, destruir la lista enlazada y otras funciones. E hizo un diagrama de pizarra para explicar el flujo de la función correspondiente, con código completo para ayudar a todos a comprender mejor.

A continuación, aprenderemos sobre las pilas y las colas. Para las deficiencias de este artículo, ¡bienvenido a guiarnos a uno o dos, y comencemos el estudio de un nuevo capítulo! ! !

Supongo que te gusta

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