Estructura de datos: implementación de lista doblemente enlazada (implementación C)

inserte la descripción de la imagen aquí

Página de inicio personal: Página de inicio personal
Columna personal: "Estructura de datos" "Lenguaje C"


prefacio

En este blog, lo que se implementará es la lista enlazada circular bidireccional líder . Esta estructura logra que la inserción y eliminación de colas solo necesite una complejidad de tiempo O (1).
Su estructura es la siguiente:

inserte la descripción de la imagen aquí


1. Idea de implementación

1. Estructura de nodo (ListNode)

Dado que la lista vinculada que se implementará es un bucle bidireccional, para el campo de puntero, necesitamos un puntero al nodo anterior y un puntero al siguiente nodo . En cuanto al bucle bidireccional, solo necesitamos que el siguiente del nodo de cola apunte al nodo principal y el anterior del nodo principal apunte al nodo de cola para realizar el bucle de dos vías.
como sigue:
inserte la descripción de la imagen aquí

typedef int LTDataType;//方便以后修改数据类型

typedef struct ListNode
{
    
    
	LTDataType data;
	struct ListNode* next;
	struct ListNode* prev;
}ListNode;

2. Cree un nuevo nodo (BuyListNode)

Abra dinámicamente un espacio, haga que el anterior y el siguiente del nodo apunten a sí mismo (para facilitar la creación de la estructura del encabezado), luego asigne un valor a los datos y devuelva la dirección del espacio.

inserte la descripción de la imagen aquí

//创建新节点
ListNode* BuyListNode(LTDataType x)
{
    
    
	ListNode* newnode = (ListNode*)malloc(sizeof(ListNode));
	if (newnode == NULL)
	{
    
    
		perror("malloc");
		exit(-1);
	}

	newnode->data = x;
	newnode->next = newnode;
	newnode->prev = newnode;

	return newnode;
}

3. La creación del nodo principal (ListCreate)

Reutilice la función BuyListNode para invalidar los datos del nodo principal (aquí -1).

//创建返回链表的头结点
ListNode* ListCreate()
{
    
    
	return BuyListNode(-1);
}

4. Destrucción de la lista doblemente enlazada (ListDestroy)

Para destruir la lista vinculada, tenemos que atravesar la lista vinculada, entonces, ¿cómo atravesar la lista vinculada?

  • La condición final es atravesar hasta el nodo principal.
  • Comience a atravesar desde el siguiente nodo del nodo principal

Como arriba, podemos recorrer la lista vinculada.
Para destruir un nodo, necesitamos dos variables de puntero, una para registrar el nodo que estará libre (cur) y otra para registrar el siguiente nodo del nodo que estará libre (curNext). Cada vez que sea libre (cur), haga cur = curNext.
inserte la descripción de la imagen aquí

//双向链表的销毁
void ListDestroy(ListNode* pHead)
{
    
    
	assert(pHead);

	ListNode* cur = pHead->next;
	while (cur != pHead)
	{
    
    
		ListNode* curNext = cur->next;
		
		free(cur);
		cur = curNext;
	}
	free(pHead);
}

5. Impresión de lista doblemente enlazada (ListPrint)

Solo necesitamos recorrer e imprimir la lista vinculada.

  • La condición final es atravesar hasta el nodo principal.
  • Comience a atravesar desde el siguiente nodo del nodo principal
//双向链表的打印
void ListPrint(ListNode* pHead)
{
    
    
	assert(pHead);

	ListNode* cur = pHead->next;
	printf("head<=>");
	while (cur != pHead)
	{
    
    
		printf("%d<=>", cur->data);
		cur = cur->next;
	}
	printf("head\n");
}

6. Inserción final de lista doblemente enlazada (ListPushBack)

Solo necesitamos dejar que el siguiente del nodo de cola (cola) apunte al nuevo nodo, el anterior del nuevo nodo apunte al nodo de cola (cola), el siguiente del nuevo nodo apunte al nodo principal y el anterior del nodo principal apunte a newnode.Sabemos que la lista vinculada es un ciclo bidireccional,
entonces El nodo anterior del nodo principal es el nodo de cola. (En comparación con la lista enlazada única, la lista enlazada no necesita recorrerse para encontrar la cola y la complejidad temporal es O (1)).
inserte la descripción de la imagen aquí

//双向链表的尾插
void ListPushBack(ListNode* pHead, LTDataType x)
{
    
    
	assert(pHead);

	ListNode* newnode = BuyListNode(x);
	ListNode* tail = pHead->prev;

	tail->next = newnode;
	newnode->prev = tail;
	pHead->prev = newnode;
	newnode->next = pHead;
}

7. Eliminación final de lista doblemente enlazada (ListPopBack)

Solo necesitamos dos punteros tail (apunta al nodo de cola) y tailPrev (apunta al nodo anterior del nodo de cola).
Libere la cola, haga que el siguiente de tailPrev apunte al nodo principal y el anterior del nodo principal apunte a tailPrev.

  • Nota: cuando head->next == head, la lista vinculada no tiene datos válidos y los datos no se pueden eliminar al final.

inserte la descripción de la imagen aquí

//双向链表的尾删
void ListPopBack(ListNode* pHead)
{
    
    
	assert(pHead);
	//pHead->next == pHead时,链表没有元素
	assert(pHead->next != pHead);

	ListNode* tail = pHead->prev;
	ListNode* tailPrev = tail->prev;

	pHead->prev = tailPrev;
	tailPrev->next = pHead;
	free(tail);
}

8. Inserción de cabecera de la lista doblemente enlazada (ListPushFront)

Solo necesitamos un puntero primero (el siguiente nodo del nodo principal), de modo que el anterior del primero apunte al nuevo nodo, el siguiente del nuevo nodo apunte al primero, el siguiente del principal apunte al nuevo nodo y el anterior del nuevo nodo apunte al encabezado .

inserte la descripción de la imagen aquí
El nodo principal es un bit centinela y no almacena datos válidos.

//双向链表的头插
void ListPushFront(ListNode* pHead, LTDataType x)
{
    
    
	assert(pHead);

	ListNode* newnode = BuyListNode(x);
	ListNode* first = pHead->next;

	newnode->next = first;
	first->prev = newnode;
	newnode->prev = pHead;
	pHead->next = newnode;
}

9. Eliminar el encabezado de la lista doblemente enlazada (ListPopFront)

Necesitamos dos punteros primero (que apunta al siguiente nodo del nodo principal), segundo (que apunta al siguiente nodo del nodo principal).
Libere el primero, de modo que el anterior del segundo apunte al nodo principal y el siguiente del nodo principal apunte al segundo.

  • Nota: Si encabezado->siguiente == encabezado, significa que la lista vinculada está vacía y el encabezado no se puede eliminar.

inserte la descripción de la imagen aquí

//双向链表的头删
void ListPopFront(ListNode* pHead)
{
    
    
	assert(pHead);
	assert(pHead->next != pHead);

	ListNode* first = pHead->next;
	ListNode* second = first->next;

	second->prev = pHead;
	pHead->next = second;
	free(first);
}

10. Búsqueda de listas doblemente enlazadas (ListFind)

Necesitamos recorrer la lista vinculada y comparar si el elemento de la lista vinculada es el objeto que se va a encontrar. Si lo encuentra, devuelva la dirección del nodo. Si no se encuentra, devuelve NULL.

//双向链表的查找
ListNode* ListFind(ListNode* pHead, LTDataType x)
{
    
    
	assert(pHead);

	ListNode* cur = pHead->next;
	while (cur != pHead)
	{
    
    
		if (cur->data == x)
			return cur;
		
		cur = cur->next;
	}

	return NULL;
}

11. La lista doblemente enlazada se inserta antes de pos (ListInsert)

Necesitamos un puntero posPrev (que apunta al nodo anterior a pos).
El anterior de pos apunta a un nuevo nodo, el siguiente de un nuevo nodo apunta a pos; el siguiente de posPrev apunta a un nuevo nodo y el anterior de un nuevo nodo apunta a posPrev.

  • Si pos apunta al nodo principal (posición centinela), entonces ListInsert equivale a completar la inserción de la cola.
  • Si pos apunta al siguiente nodo del nodo principal (posición centinela), entonces ListInsert es equivalente a la inserción del cabezal.

inserte la descripción de la imagen aquí

//双向链表在pos前进行插入
void ListInsert(ListNode* pos, LTDataType x)
{
    
    
	assert(pos);

	ListNode* newnode = BuyListNode(x);
	ListNode* posPrev = pos->prev;

	newnode->prev = posPrev;
	posPrev->next = newnode;
	newnode->next = pos;
	pos->prev = newnode;
}

12. La lista doblemente enlazada elimina el nodo en la posición pos (ListErase)

Necesitamos dos punteros posPrev (registre la dirección del nodo pos anterior), posNext (registre la dirección del siguiente nodo pos). Libere pos, haga que el anterior de posNext apunte a posPrev y el siguiente de posPrev apunte a posNext.

  • Si pos apunta al siguiente nodo principal, entonces ListErase es equivalente a la eliminación principal.
  • Si pos apunta al nodo anterior del nodo principal (es decir, el nodo de cola), entonces ListErase es equivalente a la eliminación de cola.

inserte la descripción de la imagen aquí


//双向链表删除pos位置的节点
void ListErase(ListNode* pos)
{
    
    
	assert(pos);

	ListNode* posNext = pos->next;
	ListNode* posPrev = pos->prev;

	posPrev->next = posNext;
	posNext->prev = posPrev;
	free(pos);
}

2. Implementación del código

Para las funciones de inserción de cabecera y de inserción de cola, reutilicé la función ListInsert.
Para las funciones de eliminación de encabezado y eliminación de cola, reutilicé la función ListErase.

//Lish.h 文件

#pragma once

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


typedef int LTDataType;

typedef struct ListNode
{
    
    
	LTDataType data;
	struct ListNode* next;
	struct ListNode* prev;
}ListNode;


//创建返回链表的头结点
ListNode* ListCreate();

//创建新节点
ListNode* BuyListNode(LTDataType x);

//双向链表的销毁
void ListDestroy(ListNode* pHead);

//双向链表的打印
void ListPrint(ListNode* pHead);

//双向链表的尾插
void ListPushBack(ListNode* pHead, LTDataType x);

//双向链表的尾删
void ListPopBack(ListNode* pHead);

//双向链表的头插
void ListPushFront(ListNode* pHead, LTDataType x);

//双向链表的头删
void ListPopFront(ListNode* pHead);

//双向链表的查找
ListNode* ListFind(ListNode* pHead, LTDataType x);

//双向链表在pos前进行插入
void ListInsert(ListNode* pos, LTDataType x);

//双向链表删除pos位置的节点
void ListErase(ListNode* pos);


//Lish.c  文件

#include "List.h"


//创建返回链表的头结点
ListNode* ListCreate()
{
    
    
	return BuyListNode(-1);
}


//创建新节点
ListNode* BuyListNode(LTDataType x)
{
    
    
	ListNode* newnode = (ListNode*)malloc(sizeof(ListNode));
	if (newnode == NULL)
	{
    
    
		perror("malloc");
		exit(-1);
	}

	newnode->data = x;
	newnode->next = newnode;
	newnode->prev = newnode;

	return newnode;
}


//双向链表的打印
void ListPrint(ListNode* pHead)
{
    
    
	assert(pHead);

	ListNode* cur = pHead->next;
	printf("head<=>");
	while (cur != pHead)
	{
    
    
		printf("%d<=>", cur->data);
		cur = cur->next;
	}
	printf("head\n");
}

//双向链表的尾插
void ListPushBack(ListNode* pHead, LTDataType x)
{
    
    
	assert(pHead);

	/*ListNode* newnode = BuyListNode(x);
	ListNode* tail = pHead->prev;

	tail->next = newnode;
	newnode->prev = tail;
	pHead->prev = newnode;
	newnode->next = pHead;*/
	
	ListInsert(pHead, x);
}


//双向链表的尾删
void ListPopBack(ListNode* pHead)
{
    
    
	assert(pHead);
	//pHead->next == pHead时,链表没有元素
	assert(pHead->next != pHead);

	/*ListNode* tail = pHead->prev;
	ListNode* tailPrev = tail->prev;

	pHead->prev = tailPrev;
	tailPrev->next = pHead;
	free(tail);*/

	ListErase(pHead->prev);
}


//双向链表的头插
void ListPushFront(ListNode* pHead, LTDataType x)
{
    
    
	assert(pHead);

	/*ListNode* newnode = BuyListNode(x);
	ListNode* first = pHead->next;

	newnode->next = first;
	first->prev = newnode;
	newnode->prev = pHead;
	pHead->next = newnode;*/

	ListInsert(pHead->next, x);
}


//双向链表的头删
void ListPopFront(ListNode* pHead)
{
    
    
	assert(pHead);
	assert(pHead->next != pHead);

	/*ListNode* first = pHead->next;
	ListNode* second = first->next;

	second->prev = pHead;
	pHead->next = second;
	free(first);*/

	ListErase(pHead->next);
}



//双向链表的查找
ListNode* ListFind(ListNode* pHead, LTDataType x)
{
    
    
	assert(pHead);

	ListNode* cur = pHead->next;
	while (cur != pHead)
	{
    
    
		if (cur->data == x)
			return cur;
		
		cur = cur->next;
	}

	return NULL;
}



//双向链表在pos前进行插入
void ListInsert(ListNode* pos, LTDataType x)
{
    
    
	assert(pos);

	ListNode* newnode = BuyListNode(x);
	ListNode* posPrev = pos->prev;

	newnode->prev = posPrev;
	posPrev->next = newnode;
	newnode->next = pos;
	pos->prev = newnode;
}



//双向链表删除pos位置的节点
void ListErase(ListNode* pos)
{
    
    
	assert(pos);

	ListNode* posNext = pos->next;
	ListNode* posPrev = pos->prev;

	posPrev->next = posNext;
	posNext->prev = posPrev;
	free(pos);
}


//双向链表的销毁
void ListDestroy(ListNode* pHead)
{
    
    
	assert(pHead);

	ListNode* cur = pHead->next;
	while (cur != pHead)
	{
    
    
		ListNode* curNext = cur->next;
		
		free(cur);
		cur = curNext;
	}
	free(pHead);
}

Resumir

¡Lo anterior es la realización de la lista doblemente enlazada! ! !
inserte la descripción de la imagen aquí

Supongo que te gusta

Origin blog.csdn.net/li209779/article/details/132068457
Recomendado
Clasificación