【Registro de aprendizaje de estructura de datos 4】 —— Lista doblemente enlazada

1. Breve introducción

El significado de una lista doblemente enlazada es que además de la dirección del siguiente nodo, un nodo debe almacenar adicionalmente la dirección del nodo anterior.
De esta manera, asumiendo que conoce la dirección de uno de los nodos, podemos recorrer fácilmente "hacia adelante" y "hacia atrás" en lugar de solo el recorrido "hacia atrás" para las listas enlazadas individualmente.
Por lo tanto, una lista de enlace doble es más flexible que una lista de enlace único. Tardará un poco menos en recorrer, pero costará espacio de almacenamiento adicional. Algunas preguntas pueden ser escandalosas y "avanzarán y retrocederán", por lo que la lista enlazada individualmente no puede completar estas funciones. La lista de doble enlace es un "espacio para el tiempo" muy típico.

Dos. Diagrama de ideas

2.1 Nodo

Para crear un nodo, aún debemos ser similares a una lista enlazada individualmente, porque debe contener prev data nexttres partes. prevSeñale la dirección del nodo anterior, nextseñale la dirección del siguiente nodo.
Inserte la descripción de la imagen aquí

2.2 Estructura de lista vinculada

Debido a la estructura de la lista doblemente enlazada, no puedo simplemente almacenar una 头地址, tenemos que declarar una 尾地址. En el caso de recorrido hacia adelante, el límite en el que termina el recorrido es *next = tail; en el caso de recorrido inverso, el límite en el que termina el recorrido es *prev = head. Además, para ahorrar espacio, podemos agregar un recuento (para contar la longitud de la lista vinculada). Si lo indexinsertamos como un identificador, entonces podemos indexobtener la dirección transversal a través de la longitud y comparación de la lista vinculada .
Por supuesto, si le resulta problemático, no necesita crear una lista vinculada y puede utilizar directamente nodelos datos del primer nodo para registrar la longitud de la lista vinculada.Este artículo utiliza el primer método.

Inserte la descripción de la imagen aquí

2.3 Inicialización de la lista enlazada

Es mejor crear una estructura por separado, que contenga head *tail len = 0tres partes, y asignar valores iniciales.
Inserte la descripción de la imagen aquí

2.4 recorrido de la lista vinculada

Podemos producir en orden positivo o en orden inverso, pero el "comienzo" y la "dirección del proceso" del recorrido son diferentes.
Será mejor que escribamos una función transversal que pueda determinar automáticamente la dirección, de modo que para las siguientes operaciones, podamos sacar la dirección de cualquier nodo y este código se pueda reutilizar.

2.5 Inserción de lista enlazada

La inserción de una lista doblemente enlazada es muy sencilla, solo necesitamos:

  1. El nodo a insertar NewNodese *prevcambia NextNodea *prev; *nextcambio LastNodede *next.
  2. Luego, cambie LastNodela *nextsuma del nodo anterior preva NewNodela dirección del nodo insertado , ¡y listo!
    Inserte la descripción de la imagen aquí

2.6 Eliminación de lista vinculada

Necesitamos modificar los dos nodos que lo rodean:

  1. Para LastNodeel *nextcambio de su*next
  2. Para NextNodeel *prevcambio es *prev
    por lo que no se puede atravesar, entonces el nodo ha sido borrado
    Inserte la descripción de la imagen aquí

2.7 Modificación / Consulta de la lista vinculada

Esta parte es equivalente al recorrido, solo necesita modificar el valor.

Tres. Implementación del código

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

#define OK      1
#define ERROR   0

typedef struct dataType{
    
    
    int data1;
    int data2;
}dataType;  // 数据类

typedef struct nodeType{
    
    
    struct nodeType *prev;
    struct nodeType *next;
    dataType data;
}nodeType; // 节点类

typedef struct listType{
    
    
    nodeType *head;
    nodeType *tail;
    int len;
}listType;  // 链表类

dataType DEFAULTDATA={
    
    0,0}; // 创建一个默认的类,作为数据结构

nodeType* NewNode(dataType data);
listType* ListInit(void);
nodeType* ListGetNode(listType *list, int index);
int ListShow(listType *list, int way);
int ListInsert(listType *list, dataType data, int index);
int ListDelete(listType *list, int index);
int ListModify(listType* list, dataType data, int index);
int ListQuery(listType* list, dataType data, int way);

int main()
{
    
    
    listType* list = ListInit();
    dataType test = {
    
    1,2};

    //主函数可以自己修改
    ListInsert(list, test, 0);
    test.data1 = 2;
    ListInsert(list, test, 1);
    test.data1 = 3;
    ListInsert(list,test,2);
    ListShow(list, 0);
    ListModify(list, test, 0);
    ListShow(list, 0);
    printf("i find in %d\n", ListQuery(list, test, 0));
    printf("i find in %d\n", ListQuery(list, test, 1));
    return 0;
}

nodeType* NewNode(dataType data)
{
    
    
    nodeType* node = (nodeType*)malloc(sizeof(nodeType));

    if(node != NULL)
    {
    
    
        node->data = data;  // 把数据的值赋值给节点
        node->prev = NULL;  // 并初始化节点的信息
        node->next = NULL;
        return node;
    }
    exit(1);
}

listType* ListInit(void)
{
    
    
    listType* list = (listType*)malloc(sizeof(listType));

    if (list != NULL)
    {
    
    
        list->head = NewNode(DEFAULTDATA); //   创建两个节点给头尾
        list->tail = NewNode(DEFAULTDATA);
        list->head->next = list->tail;  // 并将头的地址指向尾
        list->tail->prev = list->head;  // 尾的地址指向头
        list->len = 0;  // 初始化长度
        return list;
    }
    exit(1);
}

nodeType* ListGetNode(listType *list, int index)
{
    
    
    nodeType *begin, *end;
    int cont = 0;

    if (list->len == 0)
    {
    
    
        return list->tail; // 如果链表为空,那么就返回尾节点
    }
    else
    {
    
    
        if (index > list->len / 2)  // 通过1/2判断从前还是后遍历
        {
    
    
            begin = list->tail;
            end = list->head;
            cont = list->len;   
            // 因为len刚好比index序号大一,所以尾节点的序号刚好等于len
            // 反之,如果是头节点,那么序号就是 -1
            while(begin != end && cont > index) // 遍历的边界就是链表的末尾,或者cont= index
            {
    
    
                begin = begin->prev;
                --cont; // 遍历节点与更新当前index
            }
            return begin;  // 此时的链表节点,就是index指向的节点,或者表头
        }
        else
        {
    
    
            begin = list->head;
            end = list->tail;
            cont = -1;
            // 头节点的序号就是-1
            while(begin != end && cont < index) // 遍历的边界就是链表末尾或者cont=index
            {
    
    
                begin = begin->next;
                ++cont; // 这部分和上面一样
            }
            return begin;
        }
    }
}

int ListShow(listType *list, int way)
{
    
    
    nodeType* begin;
    nodeType* end;
    int cont = 0;

    if (way == 1)   //way=1时,从后往前遍历,否则都是从前开始遍历
    {
    
    
        begin = list->tail->prev;
        end = list->head;
        cont = list->len - 1;
        // 此时的开始节点是尾节点的前一个,要么是有效的,要么就是头节点
        // 所以开始的节点序号是len-1
        while (begin != end)
        {
    
    
            printf("No.%d is %d %d\n", cont,begin->data.data1, begin->data.data2);
            begin = begin->prev;
            --cont;
        } // 遍历,并输出信息
        printf("----------------\n");
        return OK;
    }
    else
    {
    
    
        begin = list->head->next;
        end = list->tail;
        cont = 0;
        // 从头节点的后一个节点开始,所以序号因该是0
        while (begin != end)
        {
    
    
            printf("No.%d is %d %d\n", cont,begin->data.data1, begin->data.data2);
            begin = begin->next;
            ++cont;
        } // 同理 都是遍历
        printf("----------------\n");
        return OK;
    }
}


int ListInsert(listType *list, dataType data, int index)
{
    
    
    nodeType *newNode = NewNode(data);  // 先创建一个节点
    nodeType *nextNode = ListGetNode(list, index);  // 获得index节点的地址 
    nodeType *lastNode = nextNode->prev;    // 插入节点前的节点地址

    if (nextNode->prev != NULL)
    {
    
    
        newNode->prev = nextNode->prev;     // 修改插入节点的信息
        newNode->next = nextNode;
        lastNode->next = newNode;   // 更新插入节点的前节点信息
        nextNode->prev = newNode;   // 更新插入节点的后节点信息
        list->len += 1; // 一定不要忘记把长度加1
        return OK;
    }
    else
    {
    
    
        return ERROR;
    }

}

int ListDelete(listType* list, int index)
{
    
    
    nodeType *dNode;
    nodeType *nextNode;
    nodeType *lastNode;
    if (list->len == 0)
    {
    
    
        return ERROR;
    }
    else
    {
    
    
        dNode = ListGetNode(list, index);   // 找到要删除的节点
        if (dNode->next == NULL || dNode->prev == NULL)
        {
    
    
            return ERROR;   // 确保不是头节点或者尾节点
        }
        nextNode = dNode->next; //  更新前后节点的信息
        lastNode = dNode->prev;
        lastNode->next = nextNode;
        nextNode->prev = lastNode;
        list->len -= 1; // 一定不要忘记要修改链表的长度
        return OK;
    }
}

int ListModify(listType* list, dataType data, int index)
{
    
    
    nodeType *thisNode = ListGetNode(list, index);  //查找节点

    if (thisNode->next != NULL || thisNode->prev != NULL) // 确保不是首尾节点
    {
    
    
        thisNode->data = data;
        return OK;
    }
    else
    {
    
    
        return ERROR;
    }
}

int ListQuery(listType* list, dataType data, int way)
{
    
    
    nodeType *begin;
    nodeType *end;
    int cont = 0;
    
    if (way == 0)
    {
    
    
        begin = list->head->next; // 创建开始与结束节点
        end = list->tail;
        cont = 0;   // index计数
        while(begin != end)
        {
    
    
            if (begin->data.data1 == data.data1 && begin->data.data2 == data.data2)
            {
    
    
                return cont;    //找到了
            }
            else
            {
    
    
                ++cont; // 没找到,更新节点信息
                begin = begin->next;
            }
        }
        return -1;  // 没找到
    }
    else
    {
    
    
        begin = list->tail->prev;
        end = list->head;
        cont = list->len - 1;
        while(begin != end)
        {
    
    
            if (begin->data.data1 == data.data1 && begin->data.data2 == data.data2)
            {
    
    
                return cont;
            }
            else
            {
    
    
                --cont;
                begin = begin->prev;
            }
        }
        return -1;  // 反之同上,只是顺序变化了
    }
}

Supongo que te gusta

Origin blog.csdn.net/u011017694/article/details/109318559
Recomendado
Clasificación