"Las flores flotan y el agua fluye, una especie de mal de amores y dos preocupaciones"
"No hay forma de eliminar este sentimiento, así que levanté las cejas, pero llegó a mi corazón".
contenido
prefacio
Estas dos listas enlazadas son las más utilizadas en la vida real. Una lista enlazada acíclica unidireccional sin cabecera. y una lista doblemente enlazada con encabezado.
Lista enlazada acíclica unidireccional sin cabeza : la estructura es simple y, por lo general, no se usa para almacenar datos solos. 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 la entrevista de prueba escrita . Lista enlazada doblemente circular con encabezado : La estructura es la más compleja y generalmente se usa para almacenar datos por separado. Las estructuras de datos de listas enlazadas utilizadas en la práctica son todas listas enlazadas doblemente circulares principales. Además, aunque la estructura de esta estructura es compleja, se encontrará que la estructura traerá muchas ventajas después de usar el código para implementarla, pero la implementación es simple y lo sabremos después de implementar el código.
1. Crea la estructura
Nota: el typedef funciona en la línea 7. Entonces, 5 y 6 también necesitan escribir el tipo struct ListNode.
typedef int LNDataType;
typedef struct ListNode
{
struct ListNode* prev;
struct ListNode* next;
LNDataType val;
}LN;
2. malloc nuevo nodo
Nota: Es necesario juzgar si el nodo recién abierto está vacío.
//申请一个新节点
LN* BuynewNode(LNDataType x)
{
LN* newNode = (LN*)malloc(sizeof(LN));
if (newNode == NULL)
{
printf("malloc fail");
exit(-1);
}
newNode->next = NULL;
newNode->prev = NULL;
newNode->val = x;
return newNode;
}
3. Crea un ganglio centinela
Nota : debido a que se debe cambiar el contenido del puntero de plist, es decir, se debe cambiar el punto del puntero de plist, por lo que se debe pasar la dirección de plist.
Una oración es: cuyo contenido debe cambiarse, la dirección de quien debe cambiarse .
Aquí hay un punto muy ingenioso y muy inteligente, es decir, el sucesor y el predecesor de phead se están apuntando a sí mismos (phead), aquí está el nodo de bit centinela que imita la biblioteca STL de C++.
Solo puedo admirar al gran dios que inventó estas cosas. Si el ganglio centinela se diseña de esta manera, la posterior inserción y eliminación de la cola son particularmente ingeniosas.
prueba.c
LN* plist = NULL;
ListNodeInit(&plist);
Lista.h
//初始化节点
void ListNodeInit(LN** pphead)
{
LN* newNode = BuynewNode(0);
*pphead = newNode;
(*pphead)->next = *pphead;
(*pphead)->prev = *pphead;
}
4. Tapón trasero
Nota: El motivo de la afirmación es que, aunque la lista enlazada no tenga un nodo, la lista enlazada tiene al menos una cabeza, por lo que la cabeza p no debe estar vacía.
La razón por la cual la dirección no se pasa aquí es porque no necesita cambiar el punto de plist, lo que cambia es el valor en la estructura a la que apunta plist.
La situación de inserción de cola de múltiples nodos se muestra en la figura.
La cola de un nodo.
//尾插
void ListNodePushBack(LN* phead, LNDataType x)
{
assert(phead);
LN* newNode = BuynewNode(x);
LN* tail = phead->prev;
tail->next = newNode;
newNode->prev = tail;
newNode->next = phead;
phead->prev = newNode;
}
5. Imprimir
Nota: Debido a la cabeza, cur comienza desde la segunda posición.
//打印
void ListNodePrint(LN* phead)
{
LN* cur = phead->next;
while (cur != phead)
{
printf("%d ", cur->val);
cur = cur->next;
}
printf("\n");
}
6. Eliminación de cola
Tenga en cuenta que el nodo principal no se puede eliminar. Liberar el nodo principal generará un puntero salvaje y provocará un acceso ilegal cuando se vuelva a acceder.
Así que use afirmación para afirmar que no es el primer nodo.
//尾删
void ListNodePopBack(LN* phead)
{
assert(phead);
assert(phead->next != phead);
LN* tail = phead->prev;
LN* tailPrev = tail->prev;
free(tail);
tail = NULL;
phead->prev = tailPrev;
tailPrev->next = phead;
}
7. Encabezado
Es mejor usar next para registrar el siguiente nodo. Es conveniente y claro.
//头插
void ListNodePushFront(LN* phead, LNDataType x)
{
assert(phead);
LN* newNode = BuynewNode(x);
LN* next = phead->next;
phead->next = newNode;
newNode->prev = phead;
newNode->next = next;
next->prev = newNode;
}
8. Insertar antes de la posición especificada pos
En general, cuando
sólo hay un nodo.
El siguiente código funciona en ambos casos.
//指定位置前插入,极限是头插
void ListNodeInsert(LN* pos, LNDataType x)
{
if (pos == NULL)
{
printf("没有找到这个数\n");
return;
}
LN* newNode = BuynewNode(x);
LN* tailPrev = pos->prev;
tailPrev->next = newNode;
newNode->prev = tailPrev;
newNode->next = pos;
pos->prev = newNode;
}
9. Elimine el nodo pos en la ubicación especificada
Caso normal
Limitar eliminación de cola
El siguiente código se aplica a ambos casos.
//指定位置删除
void ListNodeErease(LN* phead, LN* pos)
{
if (pos == phead || pos == NULL)
{
printf("pos指向头,或为空\n");
return;
}
LN* posPrev = pos->prev;
LN* posNext = pos->next;
posPrev->next = posNext;
posNext->prev = posPrev;
free(pos);
pos = NULL;
}
10. Destruye la lista enlazada
Nota: Esto es equivalente a gratis después de que se agote malloc. De lo contrario, se producirán pérdidas de memoria.
cur se puede dejar vacío, pero es de poca utilidad, porque cur es un parámetro formal, un parámetro formal es una copia temporal del parámetro real, y el parámetro real no se puede cambiar dejando el parámetro formal vacío. Los argumentos externos aún pueden acceder ilegalmente al espacio señalado por cur.
//链表销毁
void ListNodeDestroy(LN* phead)
{
assert(phead);
LN* cur = phead->next;
LN* next = cur->next;
while (cur != phead)
{
next = cur->next;
free(cur);
cur = NULL;
cur = next;
}
free(phead);
phead = NULL;
}