[Introducción a las estructuras de datos] Capítulo 2: Tablas lineales

Tabla de contenido

1. El concepto básico de mesa lineal.

(1) Conceptos básicos de tablas lineales 

(2) Las características de la estructura lógica de la tabla lineal. 

(3) Operaciones básicas de tablas lineales

2. Almacenamiento secuencial de tablas lineales 

(1) Definición de tipo de almacenamiento secuencial de tabla lineal 

(2) Realización de las operaciones básicas de la tabla lineal sobre la tabla secuencial

(3) Análisis del algoritmo de implementación de la tabla de secuencias

① insertar

② eliminar

③ Posicionamiento (búsqueda)

3. Enlace de almacenamiento de tablas lineales. 

(1) Tipo de definición de lista enlazada simple 

① Lista enlazada única

② Método gráfico general de lista enlazada simple

③ Definición de tipo de lista enlazada individualmente

④ Operación simple de una sola lista enlazada

(2) Implementación de las operaciones básicas de la tabla lineal en la lista enlazada simple 

① Inicialización

② Encuentra la longitud de la mesa

③ Elemento de lectura de mesa

④ Posicionamiento

⑤ insertar

⑥ eliminar

4. Implementación de otras operaciones en la lista enlazada simple 

(1) Crear una tabla 

(2) Eliminar nodos duplicados 

① Borre el nodo duplicado cuyo valor es x en la lista enlazada individualmente 

② Borrar todos los nodos duplicados en la lista de enlaces individuales 

5. Otras listas enlazadas 

(1) Lista enlazada circular 

(2) Lista enlazada circular bidireccional 

① Lista enlazada circular bidireccional 

② Definición de estructura de lista doblemente enlazada

③ Inserción de nodos en la lista doblemente enlazada

④ Eliminación de nodos en la lista doblemente enlazada

6. Comparación entre implementación de secuencia e implementación de conexión 

(1) Ventajas y desventajas de la lista lineal y la lista enlazada 

(2) Comparación del rendimiento del tiempo




1. El concepto básico de mesa lineal.

(1) Conceptos básicos de tablas lineales 

[Concepto] Una tabla lineal es una secuencia finita compuesta por n (n≥0) elementos de datos (nodos) a1, a2, ..., an.

[Notación] El número n de elementos de datos se define como la longitud de la tabla:

① Cuando n=0, se llama tabla vacía, la cual se registra como: () o (espacio directo)
② Tabla lineal no vacía (n>0), registrada como: L=(a1, a2,...,an)
  • a1 se denomina nodo inicial y an es el nodo terminal.
  • Para cualquier par de nodos adyacentes ai y ai+1 ( 1≤i<n ), ai se denomina predecesor directo de ai+1, y ai+1 se denomina sucesor directo de ai.
③ El elemento de datos ai (1≤i≤n) es solo un símbolo abstracto, y su significado específico puede ser diferente en diferentes situaciones.

【Términos básicos】

  • nodo inicial, nodo final, predecesor inmediato, sucesor inmediato, longitud de la lista lineal, lista vacía
  • L=(a1,a2,…,an)
【Aviso】
  • Solo hay un nodo inicial y un nodo final en la lista lineal
  • El nodo inicial no tiene un predecesor directo y tiene un sucesor directo.
  • Un nodo terminal tiene un predecesor inmediato y ningún sucesor inmediato.
  • A excepción de estos dos nodos, cada nodo tiene solo un predecesor inmediato y un sucesor inmediato.

(2) Las características de la estructura lógica de la tabla lineal. 

Para una lista lineal no vacía: los nodos en la lista lineal tienen una relación de uno a uno

  1. Hay uno y solo un nodo inicial a 1 , sin predecesor directo, y uno y solo un sucesor  directo a 2
  2. Hay uno y solo un nodo terminal a n , sin sucesor directo, y uno y solo un predecesor  directo a n-1
  3. Los nodos internos restantes a i (2≤i≤n-1) tienen  y solo tienen un predecesor directo a i-1  y un sucesor directo  a i+1

(3) Operaciones básicas de tablas lineales

Operaciones básicas de tablas lineales
inicialización Iniciado (L) Cree una tabla vacía L=(), L no contiene elementos de datos.
Encuentra la longitud de la tabla Longitud (L) Devuelve la longitud de la lista lineal L.
buscar celda Obtener (L, i) Devuelve el i-ésimo elemento de datos de la tabla lineal y devuelve un valor especial cuando i no satisface 1≤i≤Length(L).
posición Ubicar (L,x) Encuentre el número de serie del nodo cuyo valor de elemento de datos es igual a x en la tabla lineal. Si hay varios valores de elementos de datos iguales a x, el resultado de la operación es el valor mínimo de los números de serie entre estos nodos. Si el no se puede encontrar el nodo, el resultado de la operación es 0 .
insertar Insertar (L,x,i) Inserte un nuevo elemento de datos con el valor x antes del i-ésimo elemento de datos de la tabla lineal L, y el rango de valores legales del parámetro i es 1≤i≤n+1.
Después de la operación, la tabla lineal L cambia de (a1, a2, ..., ai-1, ai, ai+1, ..., an) a (a1, a2, ..., ai-1, x , ai, ai+1, ..., an), suma 1 a la longitud de la tabla.
borrar Eliminar (L, i) Elimine el i-ésimo elemento de datos ai de la tabla lineal L, el rango de valores válidos de i es 1≤i≤n.
Después de la eliminación, la tabla lineal L cambia de (a1, a2, ..., ai-1, ai, ai+1, ..., an) a (a1, a2, ..., ai-1, ai+ 1, ..., an), la longitud de la tabla se reduce en 1.


2. Almacenamiento secuencial de tablas lineales 

(1) Definición de tipo de almacenamiento secuencial de tabla lineal 

  • El método de almacenamiento secuencial de la tabla lineal es: los nodos de la tabla se almacenan en un grupo de unidades de almacenamiento continuo en la memoria de la computadora en secuencia, y la relación de adyacencia de los elementos de datos en la tabla lineal determina su ubicación de almacenamiento en el almacenamiento. espacio, es decir, la estructura lógica Las ubicaciones de almacenamiento de los nodos adyacentes también son adyacentes.
  • Una tabla lineal implementada con almacenamiento secuencial se denomina tabla secuencial.
  • Las matrices se utilizan generalmente para representar tablas secuenciales.

[Ejemplo] Estructura de almacenamiento secuencial de una tabla lineal

  • Supongamos que hay un conjunto de datos y hay un orden entre los datos:
  • El orden de los datos aquí significa la relación lógica entre los datos, es decir, la relación lineal. Este conjunto de datos es una tabla lineal:
  • Suponiendo que la dirección de a1 se conoce como Loc(a1), y cada dato ocupa c unidades, se calcula la dirección ai:
    Loc(ai) = Loc(a1) + c*(i-1)

  • El orden de los datos aquí significa la relación lógica entre los datos, es decir, la relación lineal. Este conjunto de datos es una tabla lineal:
  • Al almacenar tablas lineales de forma secuencial, debe almacenar: tamaño de la unidad de almacenamiento, número de datos
  • Tamaño de la mesa lineal: 10        MaxSize
    Longitud de la mesa lineal: 7          Longitud
    El tipo de datos almacenados:    DataType

[Ejemplo] Definición de estructura de tabla de secuencia

【Código de ejemplo】

//定义常量Maxsize并初始化为100
const int Maxsize = 100;

//定义结构体Seqlist,包含一个数组和一个int类型的长度
typedef struct
{ 
   DataType data[Maxsize]; //数组,用于存储数据
   int length;             //当前数组中数据的个数
} Seqlist;

Seqlist L; //定义一个Seqlist类型的变量L

【Explicación del código】

  • El código define una estructura Seqlist, que contiene una matriz de datos y una longitud de tipo int, utilizada para almacenar datos y la cantidad de datos en la matriz actual.
  • Al mismo tiempo, se define un Maxsize constante y se usa un typedef para dar a Seqlist un alias para uso futuro.
  • Finalmente, se define una variable L de tipo Seqlist.
  1. const int Maxsize = 7;
    1. Use la palabra clave const para definir una constante de entero Maxsize, lo que indica que la longitud máxima de la matriz que almacena datos en la tabla de secuencia es 7
  2. estructura typedef
    1. Use la palabra clave struct para definir un tipo de estructura llamado DataType
  3. {
    1. Llaves al comienzo de una definición de estructura
  4. int si;
    1. Una variable miembro en la estructura, que representa el ID de estudiante del estudiante.
  5. nombre de char[8];
    1. La variable miembro en la estructura representa el nombre del estudiante, y la longitud del nombre es de 8 caracteres.
  6. char sexo[2];
    1. La variable miembro en la estructura indica el género del estudiante, y la longitud del género es de 2 caracteres
  7. edad int;
    1. Una variable miembro en la estructura, que representa la edad del estudiante.
  8. puntaje int;
    1. La variable miembro en la estructura indica el puntaje de ingreso del estudiante
  9. } Tipo de datos;
    1. Las llaves al final de la definición de la estructura, seguidas de la definición del alias DataType, que representa el alias del tipo de estructura DataType
  10. estructura typedef
    1. Use la palabra clave struct para definir un tipo de estructura llamado seqList
  11. {
    1. Llaves al comienzo de una definición de estructura
  12. Datos de tipo de datos [Tamaño máximo];
    1. La variable miembro en la estructura representa la matriz que almacena los datos, la longitud máxima es Maxsize
  13. longitud int;
    1. La variable miembro en la estructura indica el número de datos en la matriz actual, es decir, la longitud real de la tabla lineal
  14. } listaSeq;
    1. Las llaves al final de la definición de la estructura, seguidas de la definición del alias seqList, que representa el alias del tipo de estructura seqList
  15. seqList estudiante;
    1. Defina una variable de tabla de secuencia estudiante, que es una variable de estructura de tipo seqList, que contiene una matriz de datos y una variable miembro de longitud, que se puede usar para almacenar la información de varios estudiantes, es decir, número de estudiante, nombre, género, edad y grado, etc. información.

【Ilustración】

【en conclusión】

  • La tabla de secuencia es una tabla lineal implementada con una matriz unidimensional, y el subíndice de la matriz se puede considerar como la dirección relativa del elemento.
  • Los elementos lógicamente adyacentes se almacenan en celdas que también son físicamente adyacentes
[Características] Características de la estructura de almacenamiento secuencial:
  • La estructura lógica de la tabla lineal es consistente con la estructura de almacenamiento
  • Puede implementar lecturas aleatorias en elementos de datos

【Ilustración】 

【expresión】

  • Suponiendo que todos los nodos de la tabla lineal son del mismo tipo, el espacio de almacenamiento que ocupa cada nodo también es el mismo.
  • Suponga que cada nodo de la tabla ocupa L unidades de almacenamiento y que la dirección de almacenamiento de la primera unidad es la dirección de almacenamiento del nodo.
  • Y supongamos que la dirección de almacenamiento del nodo inicial a1 en la tabla es d, luego la dirección de almacenamiento LOC(ai) del nodo ai:
    LOC(ai)=d+(i-1)*L

(2) Realización de las operaciones básicas de la tabla lineal sobre la tabla secuencial

Implementación de operaciones básicas en tablas de secuencia:

  • insertar
  • borrar
  • posición

 

Ventajas de las tablas secuenciales:
  • No es necesario agregar espacio de almacenamiento adicional para representar la relación lógica entre los nodos
  • Se puede acceder fácilmente a cualquier nodo de la tabla de forma aleatoria
Desventajas de las tablas secuenciales:
  • Las operaciones de inserción y eliminación son inconvenientes y se debe mover una gran cantidad de nodos
  • La tabla de secuencia requiere espacio continuo y la asignación de almacenamiento solo se puede hacer por adelantado, por lo que cuando la longitud de la tabla cambia mucho, es difícil determinar el tamaño de almacenamiento adecuado.

Conclusión del análisis de inserción y eliminación: 

  • La tabla lineal representada por la estructura de almacenamiento secuencial necesita mover aproximadamente la mitad de los elementos de datos en promedio al insertar o eliminar.
  • Cuando la cantidad de elementos de datos en la tabla lineal es grande y, a menudo, se inserta o elimina, se debe considerar este punto. 【ilustrar】
  • De acuerdo con la definición anterior, el nombre de la tabla de secuencia es estudiante, la longitud máxima de la tabla es 7 y el valor de longitud real de la tabla está en estudiante.longitud

(3) Análisis del algoritmo de implementación de la tabla de secuencias

① insertar

  • La operación de inserción de una tabla lineal se refiere a insertar un nuevo nodo x en la posición i-ésima (1≤i≤n+1) de la tabla para hacer una tabla lineal con una longitud de n:
    (a1,…,ai-1,ai,…an)
  • se convierte en una lista lineal de longitud n+1:
    (a1,…,ai-1,x,ai,…an)
    ① Cuando el espacio de la tabla está lleno, no se pueden realizar más operaciones de inserción
    ② Cuando la posición de inserción es una posición ilegal, no se puede realizar la operación de inserción normal

Proceso de operación de inserción de tabla de secuencia:

  • Mueva los nodos en las posiciones n, n-1, ..., i en la tabla a las posiciones n+1, n, ..., i+1 a su vez, y deje la i-ésima posición
  • Inserte un nuevo nodo x en esta posición. Solo cuando la posición de inserción i=n+1, no es necesario mover el nodo, inserte directamente x al final de la lista
  • La longitud de la tabla de secuencia más 1
  • La siguiente figura es un diagrama esquemático de la inserción de un nuevo nodo x=66 en la posición 3:

【Diagrama esquemático】

 

[Ejemplo]  Inserte un elemento de datos en L la primera posición de :ix

  • Antes de insertar, es necesario juzgar si la mesa está llena y si la posición de inserción es legal
  • Una vez completada la inserción, los siguientes elementos deben moverse hacia atrás una posición, agregando así un nuevo elemento de datos a la tabla de secuencia

[Descripción del algoritmo específico]

//在顺序表L的第i个位置插入元素x
void InsertSeqlist(SeqList L, DataType x, int i)
{   //将元素x插入到顺序表L的第i个数据元素之前
    //检查表是否已经满
    if (L.length == Maxsize) 
        exit("表已满");
    
    //检查插入位置i是否合法
    if (i < 1 || i > L.length+1)
        exit("位置错");
    
    //将i后面的每个元素都向后移一个位置
    for (j = L.length; j >= i; j--)   //初始i=L.length
        L.data[j] = L.data[j - 1];    //依次后移
    
    //将x插入到下标为i-1的位置
    L.data[i - 1] = x;
    
    //表长度加1
    L.length++;
}

【Explicación del código】

  • La función de esta función es insertar el elemento x en la i-ésima posición de la lista de secuencia L.
  • En la función, primero verifique si la tabla está llena y, si lo está, el programa finalizará.
  • En segundo lugar, verifique si la posición de inserción i es legal, si i no está dentro del rango de 1 ~ L.longitud + 1, luego finalice el programa.
  • A continuación, cada elemento que sigue a i se desplaza hacia atrás una posición para dejar espacio para el nuevo elemento x.
  • Finalmente, inserte x en la posición cuyo subíndice es i-1 y agregue 1 a la longitud de la tabla.
  1. void InsertSeqlist(SeqList L, DataType x, int i)

    • Nombre de la función:InsertSeqlist
    • Tipo de valor de retorno: sin valor de retorno, la función de esta función es modificar directamente la tabla de secuencia L
    • Tipo de parámetro:
      • SeqList L: variable de tabla de secuencia
      • DataType x: el valor del elemento de datos a insertar
      • int i: La posición del elemento de datos que se insertará en la tabla de secuencia
  2. if (L.length == Maxsize)

    • Comprobar si la mesa está llena
    • Si la tabla de secuencia  L está llena, significa que no se pueden insertar nuevos elementos. En este momento, el programa utilizará  exit() la función del sistema para finalizar la operación y generar  "表已满" un mensaje de error.
  3. if (i < 1 || i > L.length+1)

    • Determinar si la posición de inserción de elementos de datos es legal
    • Si  i el valor del parámetro interno es menor que 1 o mayor  L.length+1, significa que la posición de inserción es ilegal. En este momento, el programa usará  exit() la función del sistema para finalizar la operación y generar  "位置错" un mensaje de error.
  4. for (j = L.length; j >= i; j--)

    • Comenzando con el último elemento de datos, mueva  i cada elemento después de la posición del elemento th una posición hacia atrás para dejar espacio para la inserción de un nuevo elemento de datos
    • j Comience a recorrer desde  L el último elemento de la tabla de secuencia hasta la posición  i - 1,  se debe mover un total L.length - (i-1) de elementos
  5. L.data[j] = L.data[j - 1];

    • j-1 Mueva el valor del elemento th  en la tabla de secuencia  a la derecha una posición, es decir, asigne el valor al j elemento th en la tabla de secuencia
  6. L.data[i - 1] = x;

    • Inserte el elemento funcional que se insertará  x en la posición  i-1 y complete la operación de inserción en la tabla de secuencia
  7. L.length++;

    • Dado que se inserta un elemento de datos en la tabla de secuencia, la cantidad de elementos en la tabla de secuencia debe aumentarse  L.length en uno para actualizar la cantidad de elementos de datos.

Análisis del algoritmo de inserción: 

  • Suponiendo que hay n elementos de datos en la tabla lineal, hay n+1  posiciones que se pueden insertar al insertar 
  • La probabilidad de insertar datos en cada posición es: 1/(n+1)
  • Al insertar en la posición i, mueva los datos n-i+1 
  • Suponiendo que la posibilidad de insertar elementos en n+1 posiciones es igual, el número medio de elementos en movimiento es:
  • Complejidad temporal media O(n):

② eliminar

  • La operación de borrado de una tabla lineal se refiere a borrar el i-ésimo nodo de la tabla para hacer una tabla lineal con una longitud de n:
    (a1,…,ai-1,ai,ai+1,…,an)
  • Conviértase en una lista lineal de longitud n-1:
    (a1,…,ai-1,ai+1,…,an)
  • Cuando la posición i del elemento que se va a eliminar no está dentro del rango de longitud de la tabla (es decir, i<1 o i>L->longitud), es una posición no válida y no se pueden realizar las operaciones normales de eliminación.

 Proceso de operación de eliminación de tabla de secuencia:

  1. Si i=n, ​​simplemente elimine el nodo terminal sin mover el nodo
  2. Si 1≤i≤n-1, los nodos en las posiciones i+1, i+2,...,n en la tabla deben moverse hacia adelante a las posiciones i, i+1,...,n-1 para para llenar las brechas causadas por las operaciones de eliminación
  3. La longitud de la mesa se reduce en 1
  • Solo cuando se elimina la posición i = n, no es necesario mover el nodo y la longitud de la tabla se puede establecer directamente en -1
【Diagrama esquemático】

[Ejemplo]L  Elimine i el elemento de datos en la posición then la tabla de secuencia 

  • Primero, debe juzgar si el puesto es legal (nota, aquí se cuenta desde 1)
  • Si la posición es válida, mueva el elemento detrás de la posición hacia la izquierda una posición, eliminando así el elemento de datos en la posición y reduciendo la longitud de la tabla de secuencia en uno.
  • Si la posición es ilegal, el programa se terminará directamente y se emitirá un mensaje de error.

[Descripción del algoritmo específico]

//删除线性表L中的第i个数据结点
void DeleteSeqList(SeqList L, int i) 
{
    //检查位置是否合法
    if (i < 1 || i > L.length)
        exit("非法位置");
     
    //将i后面的每个元素向左移动一个位置
    for (j = i; j < L.length; j++)   //第i个元素的下标为i-1
        L.data[j - 1] = L.data[j];   //依次左移
    
    //表长度减1
    L.length--;
}

【Explicación del código】

  • La función de esta función es eliminar el i-ésimo nodo de datos en la tabla lineal L.
  • La función primero verifica si la ubicación es legal y, si la ubicación no lo es, el programa finaliza.
  • Cambia cada elemento que sigue a i una posición a la izquierda para dejar espacio para el elemento eliminado x.
  • Finalmente, el elemento se elimina y la longitud de la tabla se reduce en 1.
  1. void DeleteSeqList(SeqList L, int i)

    • Nombre de la función:DeleteSeqList
    • Tipo de valor de retorno: sin valor de retorno, la función de esta función es modificar directamente la tabla de secuencia L
    • Tipo de parámetro:
      • SeqList L: variable de tabla de secuencia
      • int i: La posición del elemento que se eliminará en la lista de secuencias
  2. if (i < 1 || i > L.length)

    • Determinar si la posición del elemento a eliminar es legal
    • Si la posición  i es menor que 1 o  i mayor que la longitud de la tabla de secuencia  L.length, significa que la posición que se va a eliminar es ilegal. En este momento, el programa utilizará  exit() la función del sistema para finalizar la operación y generar  "非法位置" un mensaje de error.
  3. for (j = i; j < L.length; j++)

    • Comenzando desde la posición del elemento que se va a eliminar  i , mueva cada elemento detrás de él hacia la izquierda una posición, de modo que la  i tabla de secuencia eliminada aún mantenga una estructura de almacenamiento continua.
    • j Desde  i el comienzo del bucle hasta  L el último elemento de la lista de secuencias,  es necesario mover un total L.length - i de elementos
  4. L.data[j - 1] = L.data[j];

    • j Mover el valor del primer elemento  de la tabla de secuencias  hacia la izquierda una posición, es decir, asignar el valor al primer j-1 elemento de la tabla de secuencias
  5. L.length--;

    • Dado que un elemento de datos se elimina de la tabla de secuencia, la cantidad de elementos en la tabla de secuencia debe reducirse  L.length en uno para actualizar la cantidad de elementos de datos.

Análisis del algoritmo de borrado: 

  • Suponiendo que hay n elementos de datos en la tabla lineal, hay n  posiciones para eliminar al realizar la operación de eliminación
  • La probabilidad de borrar datos en cada ubicación es: 1/n
  • Al eliminar en la posición i, mueva los datos ni
  • Suponiendo que la posibilidad de eliminar elementos en n posiciones es igual, el número promedio de elementos en movimiento es:
  • Al realizar una operación de borrado, suponiendo que la posibilidad de borrar cada elemento es igual, el número medio de elementos en movimiento es:
  • Complejidad temporal media O(n):

③ Posicionamiento (búsqueda)

La función de la operación de posicionamiento  LocateSeqlist(L,X)  es buscar el valor mínimo del número de secuencia del nodo en L cuyo valor es igual a X, y el resultado es 0 cuando no existe tal nodo.
[Ejemplo] L  Encuentre el elemento cuyo valor está en  la tabla de secuencia  x  y devuelva su posición en la tabla de secuencia:
  • Primero establezca la posición de inicio de búsqueda en 0
  • Luego recorra la lista ordenada para encontrar  x un elemento con valor
  • Devuelve su posición si se encuentra, 0 si no se encuentra
【Diagrama esquemático】

[Descripción del algoritmo específico] Comience desde el primer elemento a1 y compárelo con x secuencialmente hasta encontrar un elemento de datos igual a x, luego devuelva su subíndice de almacenamiento o número de serie en la tabla de secuencia; o busque en toda la tabla y no encuentre coincide con elementos iguales de x, devuelve 0:

//在顺序表L中查找值为x的元素
int LocateSeqlist(SeqList L, DataType x)
{
    int i = 0;
    //在顺序表中查找值为x的结点
    while ((i < L.length) && (L.data[i] != x))
        i++;
    
    //若找到值为x的元素,返回元素的序号
    if (i < L.length)
        return i + 1;
    //未查找到值为x的元素,返回0
    else 
        return 0;
}

【Explicación del código】

  • El propósito de esta función es encontrar el elemento con valor x en la lista ordenada L.
  • En la función, el elemento con el valor x se busca en la tabla de secuencia L a través del ciclo while, y si se encuentra el elemento con el valor x, se devuelve el número de secuencia del elemento.
  • Devuelve 0 si no se encuentra ningún elemento con valor x.
  • Para calcular la longitud de la tabla de la tabla de secuencias, simplemente envíe  L.length directamente  .
  1. int LocateSeqlist(SeqList L, DataType x)

    • Nombre de la función:LocateSeqlist
    • Tipo de valor de retorno: int, el valor de retorno indica la posición del elemento encontrado en la tabla de secuencia
    • Tipo de parámetro:
      • SeqList L: variable de tabla de secuencia
      • DataType x: el valor del elemento a buscar
  2. int i = 0;

    • Defina una variable entera  ique represente la posición de la búsqueda en la tabla de secuencia actual
  3. while ((i < L.length) && (L.data[i] != x))

    • Bucle para averiguar si la tabla de secuencia contiene  x un elemento cuyo valor de elemento es
      • Cuando  es menor que la longitud de la tabla de secuencia, y  el valor del elemento correspondiente  a  i la posición actual  no lo es, continúa buscando, es decir, encuentra el primer  elemento cuyo valor está en la tabla de secuenciaixx
  4. i++;

    • Agregue constantemente 1 a la posición de búsqueda actual y continúe buscando hacia atrás hasta  encontrar x un elemento con un valor de 1
  5. if (i < L.length)

    • Si la posición de búsqueda actual  i es menor que la longitud de la tabla de secuencia
  6. return i + 1;

    • Devuelve la posición del elemento cuyo valor se encuentra  x en la tabla de secuencia, contando desde 1
  7. else

    • Si no se encuentra  ningún x elemento en la lista ordenada con valor
  8. return 0;

    • Devuelve 0, indicando que no se encontró  ningún x elemento con valor
Al analizar el algoritmo de implementación de tablas secuenciales de una tabla lineal, un indicador importante es el número de comparaciones y movimientos de elementos de datos.
1. Sea la longitud de la tabla longitud = n. En el algoritmo de inserción, el número de movimientos de los elementos no solo está relacionado con la longitud n de la tabla de secuencia, sino también con la posición insertada i.
  • El algoritmo de inserción tiene una complejidad temporal de O(n) en el peor de los casos  .
  • En general, el número de movimientos y comparaciones de elementos es  n-i+1  veces, y el número medio de movimientos del algoritmo de inserción es aproximadamente  n/2 , y su complejidad temporal es O(n) .
2. Elimine el algoritmo DeleteSeqlist, puede obtener:
  • En el peor de los casos, el número de movimientos de elementos es  n-1 y la complejidad de tiempo es O(n) El número promedio de movimientos de elementos es de aproximadamente (n-1) /2 y la complejidad de tiempo es  O(n) .
3. Para el algoritmo de posicionamiento, es necesario escanear los elementos de la tabla de secuencias.
  • La operación estándar es comparar el parámetro x con el valor del nodo en la tabla, y la complejidad de tiempo promedio es O(n) . 
  • La complejidad temporal del algoritmo  para encontrar la longitud de la tabla y leer los elementos de la tabla es O(1) , que ha alcanzado el nivel más bajo en términos de orden.


3. Enlace de almacenamiento de tablas lineales. 

La lista lineal almacenada en el modo de enlace se denomina lista enlazada para abreviar:
  • Lista de enlaces
El almacenamiento específico de la lista enlazada se expresa como:
  • Use un conjunto arbitrario de celdas de memoria para almacenar
  • El orden lógico y el orden físico de los nodos en la lista enlazada no son necesariamente los mismos. también debe almacenar información de dirección indicando su nodo sucesor

 


(1) Tipo de definición de lista enlazada simple 

① Lista enlazada única

【Diagrama esquemático】 

【ilustrar】

  • campo de datos: el campo de datos que almacena el valor del nodo
  • siguiente campo: el campo de puntero (campo de cadena) que almacena la dirección (ubicación) del sucesor directo del nodo
  • Todos los nodos están vinculados por punteros para formar una lista vinculada individualmente
  • NULL se llama: puntero nulo
  • Head se llama: variable de puntero de cabeza, que almacena la dirección del primer nodo en la lista enlazada

② Método gráfico general de lista enlazada simple

【ilustrar】 

  • Dado que a menudo solo prestamos atención al orden lógico entre los nodos y no nos preocupamos por la posición real de cada nodo, podemos usar flechas para representar los punteros en el dominio de la cadena, y la lista de enlaces simples se puede expresar como la siguiente figura
  • La función de agregar un nodo principal: generalmente no se almacenan datos en el primer nodo de la lista enlazada individualmente, que se denomina nodo principal , y el puntero principal se usa para almacenar la dirección del nodo.

【Diagrama esquemático】

③ Definición de tipo de lista enlazada individualmente

[Ejemplo] Se define un tipo de estructura de nodo de lista enlazada node, que contiene un campo de datos data y un campo de puntero  y nextse definen al mismo tiempo para facilitar la escritura de programas posteriores.NodeLinkList

【Diagrama esquemático】

[Descripción del algoritmo específico] 

//定义链表节点结构体
typedef struct node
{ 
    DataType data; //数据域
    struct node* next; //指向下一个节点的指针域
} Node, *LinkList;

【Explicación del código】 

  • Este código define una estructura de nodos de una lista enlazada.
  • Cada nodo contiene un campo de datos, que almacena los datos del nodo, y un campo de puntero que apunta al siguiente nodo.
  • Al mismo tiempo, la palabra clave typedef se usa para definir dos nuevos tipos, uno es Node, que representa el tipo de nodo, y el otro es LinkList, que representa el tipo de lista enlazada.
  • El tipo LinkList es un puntero al tipo de nodo.
  • Esta lista enlazada es una lista enlazada unidireccional, cada nodo contiene solo un puntero al siguiente nodo.
  1. typedef struct node

    • struct Definir un tipo de estructura  usando  palabras clavenode
  2. {

    • Llaves al comienzo de una definición de estructura
  3. DataType data;

    • Las variables miembro en la estructura representan el campo de datos del nodo.
  4. struct node* next;

    • La variable miembro en la estructura representa el campo puntero del nodo, apuntando al siguiente nodo
  5. } Node, *LinkList;

    • Las llaves al final de la definición de la estructura van seguidas de dos alias, que representan  el alias del Node tipo de estructura  y  el alias del puntero  al tipo de estructura  . Por lo tanto, usar   el puntero declarado es un puntero al nodo principal de la lista enlazada.nodeLinkListnodeLinkList

④ Operación simple de una sola lista enlazada

Características de la lista enlazada individualmente:
  • El nodo de inicio también se denomina primer nodo y no tiene predecesor, por lo que la cabeza del puntero de la cabeza está configurada para apuntar al nodo de inicio.
  • La lista enlazada está determinada únicamente por el puntero principal, y la lista enlazada individualmente puede ser nombrada por el nombre del puntero principal. Una lista enlazada cuyo nombre de puntero principal es head puede denominarse encabezado de lista.
  • El nodo terminal también se denomina nodo de cola y no tiene sucesor, por lo que el campo de puntero del nodo terminal está vacío, es decir, NULL
  • Los nodos que no sean el nodo principal son nodos de tabla
  • Para la conveniencia de la operación, no se almacenan datos en el nodo principal

【Diagrama esquemático】

【ilustrar】 

  • head es el puntero principal de la lista enlazada, por lo que es una variable de tipo puntero.
  • head almacena la dirección del nodo principal.
  • El nodo del primer elemento: cabeza->siguiente

(2) Implementación de las operaciones básicas de la tabla lineal en la lista enlazada simple 

① Inicialización

[Ejemplo] Inicializar una lista vacía enlazada individualmente

  • Mediante la asignación dinámica de espacio de memoria, cree un  Node nodo principal con un tamaño de , establezca el siguiente campo de puntero de nodo del nodo principal en un puntero nulo y, finalmente, devuelva el puntero del nodo principal, lo que indica que no hay datos en el enlace simple. lista
  1. Cree una lista L vacía enlazada individualmente, InitiateLinkList(L)
  2. Una lista vacía enlazada individualmente se compone de un puntero principal y un nodo principal
  3. Suponiendo que se ha definido la variable de puntero t, permita que t apunte a un nodo principal
  4. Y deje que el siguiente del nodo principal sea NULL

【Aviso】

  • Cuando se genera el nodo principal, la función malloc genera un nuevo nodo

[Atención especial] El formato de uso y la función de la función malloc

  • [Formato] El formato de la función malloc de la función de asignación de memoria dinámica es el siguiente:
    (数据类型*)malloc(sizeof(数据类型))
    // 示例:
    int *p;p=(int *)malloc(sizeof(int))

[Descripción del algoritmo específico] La lista vacía consta de un puntero principal y un nodo principal. El algoritmo se describe de la siguiente manera:

//初始化一个空的单链表
LinkList InitiateLinkList()
{
    LinkList head; //头指针
    head = malloc(sizeof(Node)); //动态构建一个节点,它是头节点
    head->next = NULL; //头节点的指针域为空
    return head;
}

【Explicación del código】

  • La función de esta función es inicializar una lista vacía enlazada individualmente.
  • La función primero define una cabeza de puntero de cabeza.
  • Luego use la función malloc para asignar dinámicamente un nodo principal y apunte el campo de puntero del nodo principal a NULL.
  • Finalmente, devuelva la cabeza del puntero de la cabeza.
  • En el algoritmo, la variable cabeza es el puntero de cabeza de la lista enlazada, que apunta al nodo recién creado, el nodo cabeza.
  • Una lista vacía con un solo enlace tiene solo un nodo principal y su campo de puntero es NULL.
  1. LinkList InitiateLinkList()

    • Nombre de la función:InitiateLinkList
    • Tipo de valor devuelto: LinkList, que devuelve un puntero al nodo principal de la lista vinculada
    • Tipo de parámetro: sin parámetro
  2. LinkList head;

    • Declarar un  Node puntero a tipo  headque represente el nodo principal de la lista enlazada
  3. head = malloc(sizeof(Node));

    • Use  malloc la asignación dinámica de un  Node espacio de memoria de tamaño y devuelva la dirección del espacio al puntero head
  4. head->next = NULL;

    • Establezca el campo de puntero del nodo principal de la lista vinculada en un puntero nulo, porque no hay ningún nodo en la lista vinculada excepto el nodo principal en este momento
  5. return head;

    • Devuelve un puntero al nodo principal  head, inicializando así una lista vacía enlazada individualmente

② Encuentra la longitud de la mesa

【Diagrama esquemático】

【ilustrar】 

  • En la estructura de almacenamiento de lista enlazada única, la longitud de la lista lineal es igual al número de nodos contenidos en la lista enlazada única ( excluyendo el nodo principal )

【Diagrama esquemático】

【paso】 

  • Deje que el contador j sea 0
  • Sea p el punto del nodo principal
  • Cuando el siguiente nodo no está vacío, j se incrementa en 1 y p apunta al siguiente nodo
  • El valor de j es el número de nodos en la lista enlazada, es decir, la longitud de la lista

[Ejemplo] Obtener head la longitud de

  • Defina un puntero  ppara apuntar al nodo principal de la lista vinculada, obtenga la longitud de la lista vinculada recorriendo la lista vinculada y, finalmente, utilice la longitud de la lista vinculada como el valor de retorno de la función.

【Aviso】

  • El papel de p=p->siguiente 

[Descripción del algoritmo específico] La lista vacía consta de un puntero principal y un nodo principal. El algoritmo se describe de la siguiente manera:

//获取单链表的长度
int lengthLinklist(LinkList head)
{ 
    Node* p; //定义一个指针p,用于遍历链表
    p = head; //指向链表头节点
    int j = 0; //用于记录链表长度的计数器
    while (p->next != NULL) //当指针p没有指向链表尾节点时
    { 
        p = p->next; //指针p指向下一个节点
        j++; //链表长度加1
    }
    return j; //返回链表长度
}

【Explicación del código】

  • La función de esta función es obtener la longitud de la lista enlazada individualmente.
  • Un puntero p se define en la función para recorrer la lista enlazada.
  • El puntero p comienza desde el nodo principal de la lista enlazada y recorre la lista enlazada hasta el nodo final de la lista enlazada.
  • Durante el recorrido, el contador j se usa para registrar la longitud de la lista enlazada.
  • Finalmente, devuelve el valor del contador j, que es la longitud de la lista enlazada.
  1. int lengthLinklist(LinkList head)

    • Nombre de la función:lengthLinklist
    • Tipo de valor devuelto: int, que devuelve la longitud de la lista enlazada
    • Tipo de parámetro:
      • LinkList head: Puntero al nodo principal de la lista vinculada (el nodo principal de la lista vinculada no contiene datos y el siguiente nodo es el primer nodo de la lista vinculada)
  2. Node* p;

    • Declara  Node un puntero a tipo  ppara recorrer la lista enlazada
  3. p = head;

    • Apunte el puntero  p al nodo principal de la lista vinculada y comience a atravesar desde el encabezado de la lista vinculada
  4. int j = 0;

    • Declarar una variable entera  j, utilizada para registrar el contador de la longitud de la lista enlazada
  5. while (p->next != NULL)

    • Cuando el puntero  p no apunta al nodo final de la lista enlazada (es decir,  p el siguiente nodo no está vacío)
  6. p = p->next;

    • Apunte el puntero  p al siguiente nodo, es decir,  p apunte al siguiente nodo en la lista enlazada
  7. j++;

    • El contador de longitud de la lista enlazada  j aumenta en 1, lo que indica que se ha atravesado un nodo actualmente
  8. return j;

    • Después del recorrido, devuelva la longitud de la lista enlazada, es decir,  j el valor del contador

③ Elemento de lectura de mesa

【Diagrama esquemático】

[Paso] Encuentra el i-ésimo nodo

  • Deje que el contador j sea 0
  • Sea p el punto del nodo principal
  • Cuando el siguiente nodo no está vacío y j<i, j se incrementa en 1 y p apunta al siguiente nodo
  • Si j es igual a i, entonces el nodo al que apunta p es el i-ésimo nodo que se encuentra; de lo contrario, no hay i-ésimo nodo en la lista enlazada

[Ejemplo] Busque el primer nodo en la lista vinculada i y devuelva el puntero del nodo; si no se encuentra, devuelva un puntero nulo

[Descripción del algoritmo específico]

// 获取链表中第 i 个节点的指针
Node* GetlinkList(LinkList head, int i) {
    Node* p;
    p = head->next; // 将 p 指向链表的第一个节点
    int c = 1; // 用 c 记录当前节点位置
    while ((c < i) && (p != NULL)) // 当前节点位置小于 i 且 p 不为空时
    {
        p = p->next; // 将 p 指向下一个节点
        c++; // 位置加一
    }
    if (i == c) // 如果当前节点位置等于 i,则返回当前节点的指针
        return p;
    else
        return NULL; // 否则返回空指针
}

【Explicación del código】

  1. Node* GetlinkList(LinkList head, int i)

    • Nombre de la función:GetlinkList
    • Tipo de valor devuelto: Node*, que es  Node un puntero para escribir
    • Tipo de parámetro:
      • LinkList head: Puntero al nodo principal de la lista vinculada (el nodo principal de la lista vinculada no contiene datos y el siguiente nodo es el primer nodo de la lista vinculada)
      • int i: La posición del nodo que se encuentra en la lista enlazada
  2. Node* p;

    • declara un  Node puntero para escribir p
  3. p = head->next;

    • Apunte el puntero  p al siguiente nodo de la lista vinculada  head , que es el primer nodo de la lista vinculada
  4. int c = 1;

    • Defina una variable  cpara registrar la posición del nodo actualmente procesado en la lista enlazada, inicializada a 1, es decir, la posición del primer nodo
  5. while ((c < i) && (p != NULL))

    • Introducir un bucle while
    • La condición del bucle es: la posición del nodo procesado actualmente en la lista vinculada es menor que la posición que se buscará  i y el puntero actual  p no está vacío
    • Cuando finalice el bucle, p apuntará al nodo de destino o estará vacío
  6. p = p->next;

    • En el ciclo, apunte el puntero  p al siguiente nodo cada vez
  7. c++;

    • En el ciclo,  c agregue 1 a la variable cada vez, lo que indica que  p la posición del nodo apuntado por el puntero actual en la lista vinculada aumenta en 1
  8. if (i == c)

    • Determinar si la posición del nodo actual en la lista enlazada es igual a la posición que se va a encontrar i
  9. return p;

    • Si la posición del nodo actual en la lista vinculada es igual a la posición que se va a buscar  i, devolver directamente el puntero del nodo p
  10. else

    • Si no se cumple la condición, se devuelve un puntero nulo  NULL, lo que indica que no se encontró el nodo de destino

④ Posicionamiento

  • La operación de posicionamiento es encontrar la posición del elemento dado el valor del elemento de la tabla.
  • Para una lista de enlaces simples, dado el valor de un nodo, averigüe qué nodo se encuentra este nodo en la lista de enlaces simples.
  • Las operaciones de posicionamiento también se conocen como búsqueda por valor.
Pasos específicos:
  1. Sea p el punto del nodo principal
  2. orden i=0
  3. Cuando el siguiente nodo no está vacío, p apunta al siguiente nodo y el valor de i aumenta en 1
  4. Hasta que el valor del nodo al que apunta p sea x, devuelve el valor de i+1
  5. Si no se encuentra ningún valor de nodo x, el valor de retorno es 0

【ilustrar】

  • La operación de posicionamiento de la tabla lineal es averiguar la posición del elemento dado el valor del elemento de la tabla.
  • En la implementación de la lista de enlaces simples, dado el valor de un nodo, averigüe qué nodo se encuentra este nodo en la lista de enlaces simples.
  • Las operaciones de posicionamiento también se conocen como búsqueda por valor.
  • En la operación de posicionamiento también es necesario visitar la lista enlazada de principio a fin hasta encontrar el nodo requerido y devolver su número de serie.
  • Devuelve 0 si no se encuentra.

[Ejemplo]head  Encuentre x el primer nodo cuyoen la lista enlazada 

[Descripción del algoritmo específico]

// 在链表中查找第一个与 x 相等的节点,返回节点的序号
// 如果不存在这样的节点,则返回 0
int LocateLinklist(LinkList head, DataType x)
{
    Node *p = head; // 将工作指针 p 指向链表头结点
    p = p->next; // 将工作指针 p 指向链表的第一个节点
    int i = 0; // 初始化结点序号为 0
    while (p != NULL && p->data != x) // 当 p 非空且 p 所指向节点的数据域不为 x 时
    {
        i++; // 结点序号加一
        p = p->next; // 工作指针指向下一个节点
    }
    if (p != NULL) // 如果 p 不为空,说明找到了相应的节点
        return i + 1; // 返回节点的序号
    else
        return 0; // 否则返回 0
}

【Explicación del código】 

  1. int LocateLinklist(LinkList head, DataType x)

    • Nombre de la función:LocateLinklist
    • Tipo de valor devuelto: int, indicando el número de serie del nodo, si no se encuentra ningún nodo correspondiente, devuelve 0
    • Tipo de parámetro:
      • LinkList head: Puntero al nodo principal de la lista vinculada (el nodo principal de la lista vinculada no contiene datos y el siguiente nodo es el primer nodo de la lista vinculada)
      • DataType x: el valor del nodo a buscar
  2. Node *p = head;

    • Declarar un  Node puntero para escribir  py apuntarlo a la lista enlazada head
  3. p = p->next;

    • Apunte el puntero  p al primer nodo de la lista enlazada
  4. int i = 0;

    • Inicializar el número de nodo a 0
  5. while (p != NULL && p->data != x)

    • Introducir un bucle while
    • La condición del bucle es: el valor del nodo procesado actualmente no es igual al valor que se va a encontrar  x, y el puntero actual  p no está vacío
    • Cuando finalice el bucle, p apuntará al nodo de destino o estará vacío
  6. i++;

    • En el bucle, agregue 1 al número de nodo cada vez, lo que indica que se ha procesado un nodo
  7. p = p->next;

    • En el ciclo, apunte el puntero  p al siguiente nodo cada vez
  8. if (p != NULL)

    • Determinar si el nodo actual está vacío
  9. return i + 1;

    • Si el nodo actual no está vacío, devuelve el número de secuencia del nodo actual más 1
  10. else

    • Devuelve 0 si el nodo actual está vacío

⑤ insertar

La operación de inserción consiste en insertar un nuevo nodo cuyo valor es x en la posición del i-ésimo nodo de la tabla, es decir, entre ai-1 y ai.
Pasos específicos:
  1. Encuentra la ubicación de almacenamiento del ai-1 p
  2. Generar un nuevo nodo *s con campo de datos x
  3. Deje que el campo de puntero del nodo *p apunte al nuevo nodo
  4. El campo puntero del nuevo nodo apunta al nodo ai

[Ejemplo] Inserte un  nuevo nodo cuyo valor esté antes del primer nodo del elemento de datos head en la lista vinculada Si la posición de inserción no existe, emite un mensaje de error y sale; de ​​lo contrario, inserte un nuevo nodo en la posiciónix

【Diagrama esquemático】

[Descripción del algoritmo específico]

// 在链表 head 的第 i 个数据元素结点之前插入一个以 x 为值的新结点
void InsertLinklist(LinkList head, DataType x, int i)
{
    Node *p, *q;
    if (i == 1)
        q = head;
    else
        q = GetLinklist(head, i - 1); // 找到第 i-1 个数据元素结点
    if (q == NULL) // 第 i-1 个结点不存在
        exit("找不到插入的位置");
    else
    {
        p = malloc(sizeof(Node)); // 生成新结点
        p->data = x; // 新结点的数据赋值为 x
        p->next = q->next; // 新结点的链域指向*q的后继结点
        q->next = p; // 修改*q的链域
    }
}

【Explicación del código】

  1. void InsertLinklist(LinkList head, DataType x, int i)

    • Nombre de la función:InsertLinklist
    • Tipo de valor devuelto: void, que no devuelve ningún valor
    • Tipo de parámetro:
      • LinkList head: Puntero al nodo principal de la lista vinculada (el nodo principal de la lista vinculada no contiene datos y el siguiente nodo es el primer nodo de la lista vinculada)
      • DataType x: el valor del nodo a insertar
      • int i: La posición que se insertará, es decir,  i para insertar un nuevo nodo antes del primer nodo del elemento de datos
  2. Node *p, *q;

    • Declara  Node un puntero para escribir, p apuntando a un nuevo nodo, q apuntando al  i-1 nodo del elemento de datos th
  3. if (i == 1)

    • Si i es igual a 1, el nuevo nodo se insertará después del nodo principal
  4. q = head;

    • Si i es igual a 1, apuntará directamente  q al nodo principal
  5. else

    • Si i es mayor que 1, debe encontrar el primer  i - 1 nodo del elemento de datos
  6. q = GetLinklist(head, i - 1);

    • Llame a  la función para devolver  el puntero del primer nodo de elemento de datos  GetLinklist en la lista vinculada i - 1q
  7. if (q == NULL)

    • Si  q es un puntero nulo, significa que la posición de inserción no existe
  8. exit("找不到插入的位置");

    • Imprimir un mensaje de error y salir del programa
  9. else

    • Si  q no es un puntero nulo, significa que la posición de inserción existe
  10. p = malloc(sizeof(Node));

    • Asigne dinámicamente un  Node espacio de memoria de tamaño y devuelva la dirección del espacio al puntero p
  11. p->data = x;

    • Asigne el campo de datos del nuevo nodo a x
  12. p->next = q->next;

    • Apunte el campo de cadena del nuevo nodo al i-ésimo nodo del elemento de datos, que es el  q nodo sucesor
  13. q->next = p;

    • Apunte el dominio de la cadena del  i-1 nodo del elemento de datos al nuevo nodo, convirtiéndolo en el  i nodo del elemento de datos nuevo.

[Nota] El orden de ejecución de las operaciones de enlace p->siguiente=q->siguiente y  q->siguiente=p  no se puede invertir; de lo contrario, el valor del dominio de la cadena del nodo *q (es decir, el valor que apunta a la i - th nodo en el puntero de la tabla original) se perderá. 

⑥ eliminar

[Ideas de algoritmos] Este algoritmo describe cómo eliminar el i-ésimo nodo
  1. Encuentre el nodo i-1, si existe, continúe, de lo contrario finalice;
  2. Elimine el i-ésimo nodo y libere la memoria correspondiente, fin.

[Pasos del algoritmo] La operación de eliminación consiste en eliminar el i-ésimo nodo de la tabla

  1. Encuentre la ubicación de memoria p de ai-1
  2. Sea p->next el punto del nodo sucesor directo de ai
  3. Libere el espacio del nodo ai y devuélvalo al "grupo de almacenamiento" 

[Explicación] La operación básica para eliminar el i-ésimo nodo en la lista de enlaces simpleses:

  • Encuentre el nodo i-1th en la lista lineal y modifique su puntero al sucesor

[Ejemplo] Eliminar  el primer nodohead en i

  • Si el nodo no existe, emite un mensaje de error y sale; de ​​lo contrario, elimine el nodo de la lista vinculada y libere el espacio de memoria que ocupa.

【Diagrama esquemático】

[Descripción del algoritmo específico]

// 删除表 head 的第 i 个结点
void DeleteLinklist(LinkList head, int i)
{
    Node *q, *p;
    if (i == 1)
        q = head;
    else
        q = GetLinklist(head, i - 1); // 先找待删结点的直接前驱
    if (q != NULL && q->next != NULL) // 若直接前驱存在且待删结点存在
    {
        p = q->next; // p 指向待删结点
        q->next = p->next; // 移出待删结点
        free(p); // 释放已移出结点 p 的空间
    }
    else
        exit("找不到要删除的结点"); // 结点不存在
}

【Explicación del código】

  1. void DeleteLinklist(LinkList head, int i)

    • Nombre de la función:DeleteLinklist
    • Tipo de valor devuelto: void, que no devuelve ningún valor
    • Tipo de parámetro:
      • LinkList head: Puntero al nodo principal de la lista vinculada (el nodo principal de la lista vinculada no contiene datos y el siguiente nodo es el primer nodo de la lista vinculada)
      • int i: la posición del nodo a eliminar, es decir, para eliminar el primer  i nodo
  2. Node *q, *p;

    • Declarar  Node punteros a tipos  q y  p, q apuntando al predecesor inmediato del nodo que se eliminará, p apuntando al nodo que se eliminará
  3. if (i == 1)

    • Si i es igual a 1, el nodo a eliminar es el nodo principal, por lo que apuntará  q al nodo principal
  4. q = head;

    • Si i es igual a 1, apuntará  q al nodo principal
  5. else

    • Si i es mayor que 1, debe encontrar el predecesor inmediato del nodo que se eliminará
  6. q = GetLinklist(head, i - 1);

    • Llame a  la función para devolver  el puntero del primer nodo de elemento de datos  GetLinklist en la lista vinculada i - 1q
  7. if (q != NULL && q->next != NULL)

    • Si el nodo predecesor inmediato  q no es un puntero nulo y existe el nodo que se eliminará
  8. p = q->next;

    • Apunte el puntero  p al nodo a eliminar, es decir,  q el nodo sucesor de
  9. q->next = p->next;

    • Apunte el siguiente nodo del puntero  q al siguiente nodo del nodo a eliminar, es decir, elimine el nodo a eliminar de la lista vinculada
  10. free(p);

    • p libera espacio para los nodos movidos 
  11. else

    • si el nodo no existe
  12. exit("找不到要删除的结点");

    • Imprimir un mensaje de error y salir del programa

[Nota] free(p) es esencial, porque cuando se elimina un nodo de la lista enlazada, si no se libera su espacio, se convertirá en un nodo inútil y siempre ocupará el espacio de memoria del sistema, otros programas no serán capaz de utilizar este espacio.



4. Implementación de otras operaciones en la lista enlazada simple 

(1) Crear una tabla 

Este proceso se divide en tres pasos:

  1. Primero cree una tabla vacía con el nodo principal
  2. En segundo lugar, cree un nuevo nodo y luego vincule el nuevo nodo al nodo principal, este nodo es el nodo final (también el primer nodo)
  3. Repita los dos pasos de crear un nuevo nodo y vincular el nuevo nodo al final de la lista hasta que todos los elementos de la lista lineal estén vinculados a la lista vinculada individualmente, aquí use int en lugar de DataType

[Método 1]  Se realiza a través del algoritmo de inserción implementado InsertLinklist (LinkList head, int x, int i) , y la posición de inserción i aumenta secuencialmente, de modo que el nuevo nodo se vincula a la lista vinculada.

[Ejemplo] La operación de crear una lista enlazada individualmente

【Código de ejemplo】

// 建立单链表
LinkList CreatLinklist()
// 通过调用InitiateLinklist和Insertlinklist实现建表算法。假定0是输入结束标志
{
    // 创建头结点
    LinkList head;
    int x, i;
    head = InitiateLinklist();      // 建立空表:初始化链表,创建头结点

    // 循环插入结点
    i = 1;                          // 置插入位置初值
    scanf("%d", &x);                // 读入第一个数据元素,x为整型
    while (x != 0)                  // 输入的不是结束标志时继续插入
    {
        InsertLinklist(head, x, i); // 将输入插入到head表尾:在链表的第i个位置插入值为x的结点
        i++;                        // 修改插入位置
        scanf("%d", &x);            // 读下一元素
    }

    return head;  // 返回创建好的单链表头结点指针
}

【Explicación del código】

  • Dentro de la función, primero llame  InitiateLinklist a la función para crear una lista enlazada vacía y cree el nodo principal  head.
  • Luego, lea los elementos de datos a través de un bucle e insértelos en la lista enlazada individualmente hasta que se lea el indicador final 0.
  • Al insertar cada vez, llame a  InsertLinklist la función para insertar el elemento de datos en el encabezado de la lista enlazada individualmente y  i agregue 1 a la posición de inserción para prepararse para la inserción del siguiente elemento de datos.
  • Finalmente, devuelva el puntero del nodo principal de la lista enlazada individualmente  head.
  1. LinkList CreatLinklist()

    • Nombre de la función:CreatLinklist
    • Tipo de valor devuelto: puntero al nodo principal de la lista enlazada individualmente, el tipo es LinkList
    • Tipo de parámetro: sin parámetro
  2. LinkList head; int x, i;

    • Defina  head, x y  i tres variables, que representan respectivamente el puntero del nodo principal de la lista enlazada individualmente, el elemento de datos de entrada y la posición de inserción
  3. head = InitiateLinklist();

    • Llame para  InitiateLinklist crear una lista enlazada vacía, es decir, inicialice la lista enlazada y cree el nodo principal, y asigne el puntero del nodo principal a head
  4. i = 1;

    • Establezca el valor inicial de la posición de inserción en 1, lo que indica que el elemento de datos se inserta en la primera posición de la lista vinculada
  5. scanf("%d", &x);

    • Lea un elemento de datos enteros de la entrada estándar y guárdelo en una  x variable
  6. while (x != 0)

    • Cuando el elemento de datos de lectura no es 0, el ciclo realiza las siguientes operaciones:
  7. InsertLinklist(head, x, i);

    •  Inserte un   nuevo nodo con valor en la posición head 1 en  la lista enlazada individualmente ix
  8. i++;

    • Incremente la posición de inserción en 1 para prepararse para la inserción del siguiente elemento de datos
  9. scanf("%d", &x);

    • Lee el siguiente elemento de datos enteros de la entrada estándar, almacenándolo en una  x variable
  10. return head;

    • Devuelva el puntero al nodo principal de la lista vinculada individualmente creada head

[Método 2] El algoritmo del método 1 comienza a buscar desde la cabecera de la tabla cada vez que se inserta, lo cual es una pérdida de tiempo: porque cada vez que se vincula un nuevo nodo al final de la tabla, podemos usar un puntero para apuntar al nodo final, de modo que indica la posición de inserción para el siguiente nodo nuevo

[Ejemplo 1] La operación de crear una lista enlazada individualmente

【Diagrama esquemático】

[Código de ejemplo] El nombre de la función CreatLinklist2, el valor de retorno es el puntero del nodo principal de la lista enlazada individualmente, y el tipo es LinkList:

// 建立单链表
LinkList CreatLinklist2()
//q是一个LinkList类型的变量,用来指示链入位置
{
    // 创建头结点
    LinkList head;
    Node *q, *t;
    int x;
    head = (Node *)malloc(sizeof(Node));      // 创建头结点

    q = head;                                 // 尾指针置初值,指向头结点
    scanf("%d", &x);                          // 读入第一个数据元素 x
    while (x != 0)                            // 输入的不是结束标志时继续插入
    {
        t = (Node *)malloc(sizeof(Node));     // 生成一个新结点
        t->data = x;                          // 给新节点赋值
        q->next = t;                          // 新节点t插入到链表中
        q = t;                                // 修改尾指针 q,指向新的尾结点
        scanf("%d", &x);                      // 读下一元素
    }

    q->next = NULL;       // q指向尾结点,置尾结点标志
    return head;          // 返回头结点指针
}

【Explicación del código】

  • Este código de función implementa la operación de crear una lista enlazada individualmente.
  • Dentro de la función, el nodo principal de la lista enlazada individualmente se crea al principio  head.
  • En el ciclo, use  malloc la función para asignar espacio de memoria, cree un nuevo nodo para el nuevo elemento de datos  ty asigne el elemento de datos de entrada a  los miembros t del  nuevo nodo data .
  • Luego, inserte un nuevo nodo  t al final de la lista vinculada y apunte el puntero de cola  q al nuevo nodo de cola  t.
  • Cuando se encuentra la marca final,  el puntero q del  nodo final next se establece en  NULL, lo que indica que la lista enlazada ha finalizado y, finalmente, se devuelve el puntero del nodo principal de la lista enlazada individual  head, lo que indica que la lista enlazada individual se ha creado correctamente.
  1. LinkList CreateLinklist2()

    • Nombre de la función:CreateLinklist2
    • Tipo de valor devuelto: puntero al nodo principal de la lista enlazada individualmente, el tipo es LinkList
    • Tipo de parámetro: sin parámetro
  2. LinkList head; Node *q, *t; int x;

    • Definición  heady  tres variables, que representan respectivamente el puntero del nodo principal, el puntero del nodo final y el puntero del nodo recién creado de la lista enlazada individualmente; la definición   representa el elemento de datos de q entrada tx
  3. head = (Node *)malloc(sizeof(Node));

    • Use  malloc la función para asignar espacio de memoria, el tamaño de memoria asignado es  Node el tamaño de la estructura y asigne la dirección de memoria asignada al puntero del nodo principal head
  4. q = head;

    • Establezca el valor inicial del puntero de cola  q en el nodo principal head
  5. scanf("%d", &x);

    • Lea un elemento de datos enteros del flujo de entrada estándar y guárdelo en una  x variable
  6. while (x != 0)

    • Siempre que el elemento de datos de entrada no sea un marcador final, haga lo siguiente:
  7. t = (Node *)malloc(sizeof(Node));

    • Cree un nuevo nodo  ty use  malloc la función para asignar espacio de memoria
  8. t->data = x;

    • t Asigna un miembro del  nuevo nodo  data , estableciéndolo en el valor del elemento de datos de entrada x
  9. q->next = t;

    • Inserte un nuevo nodo  t en la lista enlazada, es decir, coloque el nuevo nodo  detrás t del nodo de cola original  q y  q apunte al nuevo nodo t
  10. q = t;

    • Apunte el puntero de cola  q al nuevo nodo de cola t
  11. scanf("%d", &x);

    • Lee el siguiente elemento de datos enteros del flujo de entrada estándar y lo almacena en una  x variable
  12. q->next = NULL;

    • q El puntero del  nodo final  next se establece para  NULLindicar el final de la lista enlazada
  13. return head;

    • Devuelve el puntero del nodo principal de la lista de enlaces únicos  head, es decir, la lista de enlaces únicos creada con éxito

[Método 2] El algoritmo del método 1 comienza a buscar desde la cabecera de la tabla cada vez que se inserta, lo cual es una pérdida de tiempo: porque cada vez que se vincula un nuevo nodo al final de la tabla, podemos usar un puntero para apuntar al nodo final, de modo que indica la posición de inserción para el siguiente nodo nuevo

[Ejemplo 2] La operación de crear una lista enlazada individualmente

【Diagrama esquemático】

【Código de ejemplo】

// 建立单链表
LinkList CreatLinklist3()
{
    // 创建头结点
    LinkList head;                // 定义头结点
    Node *p;                      // 定义一个指向Node类型的指针,用于遍历链表
    int x;                        // 定义一个节点的数据
    head = malloc(sizeof(Node));  // 创建头结点,动态分配内存空间

    head->next = NULL;            // 头结点的next指针指向NULL
    scanf("%d", &x);              //读入节点的数据
    while (x)                     // x=0 时结束输入:如果节点数据不为0,就一直循环插入节点
    {
        p = malloc(sizeof(Node)); // 动态分配内存空间,创建新节点
        p->data = x;              // 给节点赋值
        p->next = head->next;     // 前插:插入到链表的第一个结点处
        head->next = p;           // 让头结点的next指针指向新插入的结点,从而将新节点加入到链表中
        scanf("%d", &x);          // 读入节点数据
    }

    return head; // 返回链表头结点
}

【Explicación del código】

  • Esta función se utiliza para crear una lista enlazada individualmente. Al principio de la función, se declara un valor de retorno de tipo LinkList, lo que significa que esta función devuelve el nodo principal de la lista enlazada. Cada nodo contiene un dato y un puntero a el siguiente nodo.
  • Esta función primero crea el nodo principal y luego lee continuamente los nuevos datos del nodo hasta que los datos leídos son 0.
  • Al leer datos de nuevos nodos, el programa crea un nuevo nodo y le asigna un valor, y luego lo agrega a la primera posición de la lista vinculada.
  • Aquí se usa el método de inserción de cabeza, es decir, dejar que el nuevo nodo apunte al primer nodo original y luego dejar que el nodo principal apunte al nuevo nodo.
  • Finalmente devuelva el nodo principal.

 1.  Definir el nodo principal

LinkList head;    // 定义头结点
  • En primer lugar, se define un nodo principal de tipo LinkList y el tipo LinkList se define como:
    typedef struct Node *LinkList;
  • La estructura del Nodo se define de la siguiente manera:
    struct Node {
      int data;        // 存储节点数据
      Node *next;      // 存储指向下一个节点的指针
    };

2.  Asigne dinámicamente espacio de memoria y cree nodos principales

  • Use la función malloc para asignar espacio de memoria al nodo principal
    head = malloc(sizeof(Node)); // 创建头结点,动态分配内存空间

3.  Inicialización del nodo principal

  • Apunte el siguiente puntero del nodo principal a NULL
    head->next = NULL; // 头结点的next指针指向NULL

4.  Lea los datos del nodo, cree el nodo e insértelo en la lista enlazada

  • Primero lea los datos del nodo, si los datos del nodo no son 0, el nodo se insertará en un bucle. Primero, asigne dinámicamente espacio de memoria, cree un nuevo nodo y asígnele un valor. Luego inserte el nodo recién creado en la primera posición de la lista enlazada original, es decir, deje que el siguiente puntero del nuevo nodo apunte al primer nodo original y luego deje que el siguiente puntero del nodo principal apunte al nuevo nodo. Aquí se utiliza el método de interpolación directa, por lo que cada vez que se inserta un nuevo nodo en la lista vinculada, es la primera posición original.
    scanf("%d", &x);  // 读入节点的数据
    while (x) {       // x=0 时结束输入, 如果节点数据不为0,就一直循环插入节点
      p = malloc(sizeof(Node)); // 动态分配内存空间,创建新节点
      p->data = x;              // 给节点赋值
      p->next = head->next;     // 前插:插入到链表的第一个结点处
      head->next = p;           // 让头结点的next指针指向新插入的结点,从而将新节点加入到链表中
      scanf("%d", &x);          // 读入节点数据
    }

5.  Devolver el nodo principal de la lista enlazada

  • Finalmente devuelve el nodo principal de la lista enlazada.
    return head;  // 返回链表头结点

【Aviso】

  • En este código, la función malloc se usa para asignar espacio de memoria dinámicamente, y la memoria asignada debe liberarse al final
  • En C++, no se recomienda usar malloc para asignar memoria, sino usar el operador new, que puede evitar algunos problemas de administración de memoria.
  • Además, se recomienda agregar una declaración para liberar la memoria basura del nodo en el código para evitar pérdidas de memoria.

(2) Eliminar nodos duplicados 

① Borre el nodo duplicado cuyo valor es x en la lista enlazada individualmente 

[Análisis] Borre los nodos duplicados con el valor x en la lista enlazada individualmente

【paso】 

  1. Encuentre la posición del primer nodo con valor x, p apunta a este nodo
  2. Comience a buscar hacia atrás desde el nodo señalado por p, si hay un nodo con valor x, deje que q apunte al nodo anterior a x para realizar la operación de eliminación y continúe buscando hasta el final de la lista enlazada

② Borrar todos los nodos duplicados en la lista de enlaces individuales 

【 Análisis de refinamiento paso a paso

[ pasos generales ]

  1. Cuando no se alcanza el final de la lista enlazada (cuando ai no es un nodo terminal)
  2. Eliminar el nodo cuyo valor es ai de ai+1 a un nodo
  3. yo ++
i=1
While(ai不是终端结点)
{ 删除 ai+1 到 an 结点中值为 ai 的结点
  i++
}

【 Análisis más detallado

[ más pasos de perfeccionamiento]  

  1. Cuando no se alcanza el final de la lista enlazada (cuando ai no es un nodo terminal)
  2. Eliminar el nodo cuyo valor es ai de ai+1 a un nodo
  3. j = yo
while (j<n)
{ if(aj==ai) 删除 aj
  j++
} 
i++

[Ejemplo] Una función para eliminar nodos duplicados redundantes en una lista enlazada individualmente

  • La función recibe el puntero del nodo principal de una lista vinculada, elimina todos los nodos redundantes y garantiza que el valor de cada nodo sea único

【Diagrama esquemático】

【Código de ejemplo】

// 删除表head中多余的重复结点
void PurgeLinklist(LinkList head)
{ 
    Node *p, *q, *r;  // 定义三个指向 Node 类型的指针,其中 p 为当前工作指针,q 为当前需要检查的指针,r 为需要删除的结点指针
    q = head->next;   // 初始化为当前第一个结点:q指示当前检查结点的位置,置其初值指向首结点
    while (q != NULL) // 当前检查结点*q不是尾结点时,寻找并删除它的重复结点:当当前检查指针 q 不为空时,继续循环
    {
        p = q;                  // 工作指针*p指向当前检查指针*q
        while (p->next != NULL) // 当工作指针*p的后继结点存在时:将其数据域与*q数据域比较
        { 
            if (p->next->data == q->data) // 若工作指针的下一个指针*(p->next)的数值等于当前检查指针*p的数值:若*(p->next)是*q的重复结点
            {
                r = p->next;              // 删除 r 指向待删节点
                p->next = r->next;        // 移出结点* (p->next),p->next指向原来* (p->next)的后继结点
                free(r);                  // 释放待删节点的内存空间
            }
            else 
            {
                p = p->next;              // 否则,让工作指针*p指向下一个结点,继续检查
            }
        }
        q = q->next; // 更新检查结点:更新当前检查指针*p
    }
}

【Explicación del código】

  • Este código es una función para eliminar nodos duplicados redundantes en una lista enlazada individualmente
  • La idea de implementación básica es recorrer toda la lista enlazada en función de cada nodo en la lista enlazada individualmente, comparar cada nodo con los datos de todos los nodos detrás de él y eliminar el nodo detrás de él cuando encuentre un nodo con los mismos datos.
  • La función recibe el puntero del nodo principal de una lista vinculada, elimina todos los nodos redundantes y garantiza que el valor de cada nodo sea único

  1.  Defina tres punteros al tipo de Nodo

  • Entre ellos, p es el puntero de trabajo actual, q es el puntero actual que se comprobará y r es el puntero de nodo que se eliminará
    Node *p, *q, *r; // 定义三个指向 Node 类型的指针,其中 p 为当前工作指针,q 为当前需要检查的指针,r 为需要删除的结点指针

2.  Inicializar el puntero de verificación

  • Establezca el puntero q que debe verificarse en el primer nodo común que se verificará actualmente
    q = head->next; // 初始化为当前第一个结点

3.  Bucle exterior

  • Ingrese el ciclo while externo, cuando el puntero de verificación actual q no esté vacío, continúe con el ciclo hasta que se verifique toda la lista vinculada
    while (q != NULL) // 当当前检查指针 q 不为空时,继续循环

4.  Bucle interior

  • Ingrese al bucle while interno, cuando exista el nodo sucesor del puntero de trabajo, compare su campo de datos con el campo de datos del puntero actual q para verificar. Si  el nodo sucesor de p es el nodo duplicado de q, apunte r al nodo que se eliminará, mueva el nodo  (p->siguiente) y libere el espacio de memoria del nodo que se eliminará. De lo contrario, deje que p apunte al siguiente nodo
    while (p->next != NULL) // 当工作指针的后继结点存在时

5.  Actualizar el puntero a comprobar

  • Actualice el puntero actual q que debe verificarse
    q = q->next; // 更新当前检查指针 q

6.  Liberación física del espacio de memoria

  • Después de eliminar los nodos redundantes, cuando el programa ya no necesita estos espacios de memoria, estos espacios deben liberarse a tiempo para evitar pérdidas de memoria.
    free(r); // 释放待删节点的内存空间

【Aviso】

  • Se recomienda implementar esta función utilizando los contenedores y algoritmos proporcionados por la biblioteca estándar


5. Otras listas enlazadas 

(1) Lista enlazada circular 

【ilustrar】 

  • El siguiente valor del nodo terminal de la lista enlazada ordinaria es NULL
  • El siguiente del nodo terminal de la lista enlazada circular apunta al nodo principal
  • En una lista enlazada circular, la lista enlazada completa se puede escanear desde cualquier nodo

【Diagrama esquemático】 

La forma de encontrar el nodo de cola  de  la lista enlazada ordinaria / lista enlazada circular :

  • Adjuntar un puntero posterior  al nodo de cola en la lista enlazada circular  es adecuado para operaciones de listas enlazadas que a menudo usan nodos de cabeza y cola.


(2) Lista enlazada circular bidireccional 

① Lista enlazada circular bidireccional 

[Descripción]  Establecer dos campos de puntero en la lista vinculada:

  • un puntero al nodo sucesor
  • Un puntero al nodo predecesor
  • Tal lista enlazada se llama lista doblemente enlazada.

【Diagrama esquemático】 

 

②Definición  de estructura de lista doblemente enlazada

[Definición] La definición de la estructura de la lista doblemente enlazada:

struct dbnode // 定义一个双向链表的结点结构体
{ 
    DataType data;                 // 数据域
    struct dbnode *prior, *next;   // 双向指针域
};
typedef struct dbnode *dbpointer;  // 定义一个指向 dbnode 的指针类型 dbpointer
typedef dbpointer Dlinklist;       // 定义一个指向 dbnode 的指针类型 Dlinklist
  • En este código se define una estructura dbnode, que representa un nodo de una lista doblemente enlazada. Contiene los datos del campo de datos y dos campos de puntero antes y después. anterior apunta al nodo predecesor del nodo actual y próximo apunta al nodo sucesor del nodo actual.
  • En segundo lugar, defina un puntero de tipo dbpointer que apunte a la estructura dbnode, que se utiliza para apuntar a la variable del tipo de estructura dbnode.
  • Finalmente, cambie el nombre de dbpointer a Dlinklist con un typedef para indicar que Dlinklist es un tipo de puntero a un dbnode.

[Nodo] El nodo de la lista doblemente enlazada:

  • La lista enlazada circular bidireccional es adecuada para aplicaciones en las que es necesario buscar con frecuencia el predecesor y el sucesor del nodo
  • La complejidad de encontrar el predecesor y el sucesor son ambos: O(1)
[Ejemplo] Suponga que p apunta a un nodo en la lista doblemente enlazada
  • Entonces p->anterior->siguiente es  igual   a  p->siguiente->anterior

③ Inserción de nodos en la lista doblemente enlazada

 [Explicación] Para insertar un nuevo nodo *t después del nodo señalado por p, se deben modificar 4 punteros:

  • t->anterior = p;
  • t->siguiente = p->siguiente;
  • p->siguiente->anterior = t;
  • p->siguiente=t;

【Diagrama esquemático】 

 

④ Eliminación de nodos en la lista doblemente enlazada

[Explicación] Deje que p apunte al nodo que se eliminará, eliminar *p se puede completar con la siguiente declaración:

  • p->anterior->siguiente = p->siguiente;     // la cadena posterior del nodo predecesor de p apunta al nodo sucesor de p
  • p->next->prior = p->prior;    // p 后继结点的前链指向 p 的前驱结点
  • free(p);                                // 释放 *p 的空间
  • p->prior->next = p->next;  和  p->next->prior = p->prior; 这 2 个语句的执行顺序可以颠倒

【示意图】 

 



六、顺序实现与连接实现的比较 

(1)线性表与链表的优缺点 

  • 单链表的每个结点包括数据域与指针域,指针域需要占用额外空间
  • 从整体考虑,顺序表要预分配存储空间,如果预先分配得过大,将造成浪费,若分配得过小,又将发生上溢;单链表不需要预先分配空间,只要内存空间没有耗尽,单链表中的结点个数就没有限制

(2)时间性能的比较

顺序表 链表
读表元 O(1) 读表元 O(n)
定位(找x) O(n) 定位(找x) O(n)
插入 O(n) 插入 O(n)
删除 O(n) 删除 O(n)

Supongo que te gusta

Origin blog.csdn.net/qq_39720249/article/details/131414439
Recomendado
Clasificación