[Introducción a las estructuras de datos] Capítulo 3: Pilas, colas y matrices

Tabla de contenido

1. Pila

(1) El concepto básico de la pila

① Definición

② Diagrama esquemático

③ Las características de la pila

④ Operación básica de la pila

(2) La implementación secuencial de la pila.

① Pila de secuencias y sustantivos comunes

② Definición de tipo de pila secuencial

③ Operación básica de pila secuencial

Ⅰ Inicialización  

Ⅱ Juicio de que la pila está vacía

Ⅲ Empuje

Ⅳ. 

Ⅴ Consigue el elemento superior de la pila

(3) Implementación de enlace de pila - cadena de pila

① Definición de cadena de pila

② Escriba la descripción de la pila encadenada

③ Operación básica de la pila de cadena

Ⅰ Inicialización  

Ⅱ Juicio de que la pila está vacía

Ⅲ Empuje

Ⅳ. 

Ⅴ Consigue el elemento superior de la pila

(4) Aplicación y recursividad de la pila.

① Ejemplos básicos de aplicación de la pila

② Definición de recursividad

③ Forma general de recursividad

Dos, la cola

(1) El concepto básico de cola

① Definición

② Diagrama esquemático

③ Características de la cola

④ Funcionamiento básico de la cola

(2) Implementación secuencial de colas

① cola secuencial

② Cola circular

Ⅰ Definición de cola circular 

Ⅱ Implementación de Circular Queue Loop 

Ⅲ Definición de tipo de cola circular 

Ⅳ Provisiones 

③ Operación básica de cola circular

Ⅰ Inicialización  

Ⅱ Cola de sentencias vacía

Ⅲ.Poner en cola

Ⅳ Desencolar

Ⅴ Obtener el primer elemento de la cola

(3) Implementación de enlace de la cola

① Definición de cola en cadena 

② Descripción del tipo de cola de cadena 

③ Operación básica de cola de cadena  

Ⅰ Inicialización  

Ⅱ Cola de sentencias vacía

Ⅲ.Poner en cola

Ⅳ Desencolar

Ⅴ Obtener el primer elemento de la cola

3. matriz

(1) Estructura lógica y operaciones básicas de arreglos

(2) La estructura de almacenamiento de la matriz 

① Estructura de almacenamiento —— Estructura de almacenamiento secuencial

② Fórmula de direccionamiento (principalmente almacenada en filas)

(3) almacenamiento comprimido de matriz

① Matriz especial

② Matriz especial: matriz simétrica 

Ⅰ Definición

Ⅱ Almacenamiento físico

③ Matriz especial: matriz triangular

Ⅰ Definición 

Ⅱ Matriz triangular superior

Ⅲ Matriz triangular inferior

④ Matriz especial: matriz dispersa 

Ⅰ Definición

Ⅱ trillizos

Ⅲ Mesa triple

Ⅳ Lista de secuencias triples




1. Pila

Los archivos de encabezado que describen las implementaciones de pila y cola son los siguientes:

  • Seqstack.h: La definición e implementación de la pila secuencial.
  • Lkstack.h: Definición e implementación de cadena de pila.
  • Sequeue.h: Definición de cola de secuencia y su implementación.
  • Lkqueue.h: La definición e implementación de la cola en cadena. 

(1) El concepto básico de la pila

① Definición

Stack : es una tabla lineal que solo se puede insertar y eliminar en un extremo de la tabla (el final de la tabla)
  • El final (final de la tabla) que permite la inserción y eliminación se denomina la parte superior de la pila (Top)
  • El otro extremo (cabecera) se llama la parte inferior de la pila (inferior)
  • Cuando no hay elementos en la lista, se llama pila vacía.
  • empujar  inserta un elemento en la parte superior de la pila
  • Pop  elimina un elemento de la parte superior de la pila
  • Las pilas y las colas se pueden considerar como tablas lineales especiales, que son tablas lineales con operaciones limitadas.

② Diagrama esquemático

【Ejemplo】 Una pila de libros o una pila de platos 

【Diagrama esquemático】

③ Las características de la pila

último en entrar primero en salir:

  • Los elementos de la pila se insertan en la pila en el orden de  a 1 , a 2 , a 3 , ... a , y el primer elemento que sale de la pila debe ser el elemento superior de la pila.
  • En otras palabras, la modificación de la pila se realiza según el principio de último en entrar, primero en salir.
  • Por lo tanto, la pila se denomina lista lineal de último en entrar, primero en salir ( LIFO ). 

El propósito de la pila: a menudo se usa para almacenar temporalmente datos para ser procesados. 

④ Operación básica de la pila

  1. Inicializar la pila: InitStack(S);
  2. Juzga que la pila está vacía: EmptyStack (S);
  3. Empujar en la pila: Empujar (S,x);
  4. Pop: Pop (C);
  5. Obtener la parte superior de la pila: GetTop(S);

(2) La implementación secuencial de la pila.

① Pila de secuencias y sustantivos comunes

  • Pila secuencial  , es decir, la implementación secuencial de la pila.
  • Capacidad de pila  el número máximo de elementos que se pueden almacenar en la pila
  • Puntero superior de la pila superior:  indica la posición del elemento superior actual en la pila
  • Pila vacía  cuando no hay ningún elemento en la pila, significa que la pila está vacía
  • Pila llena  cuando el espacio de la matriz está lleno, se llama pila llena
  • Subdesbordamiento  cuando la pila está vacía y luego requiere una operación de pila, se denomina "subdesbordamiento"
  • Desbordamiento  cuando la pila está llena, se denomina "desbordamiento" si se requiere realizar una operación de inserción.

② Definición de tipo de pila secuencial

[ Tipo de pila secuencial ]

const int maxsize = 6; // 定义常量,表示栈的最大大小

typedef struct seqstack {
    DataType data[maxsize]; // 定义存储元素的数组
    int top; // 顶部指针,指向栈顶元素下标
} SeqStk; // 定义顺序栈的结构体

【Diagrama esquemático】

【Explicación del código】

  • En este código, definimos una estructura de pila secuencial  seqstack, que contiene una matriz de elementos de almacenamiento  data y un puntero superior  top. El puntero superior  top se usa para indicar la posición del subíndice del elemento superior de la pila, y la matriz que almacena los elementos  data se usa para almacenar los elementos en la pila.
  • Además, usamos  const int la palabra clave para definir una constante  maxsize, que indica el tamaño máximo de la pila, para que la constante se pueda usar convenientemente en todo el programa.
  • Tenga en cuenta que en el código anterior  DataType hay un marcador de posición que indica el tipo de datos, puede reemplazarlo con el tipo de datos que necesita de acuerdo con sus necesidades reales.

【ilustrar】 

SeqStk *s ; /*定义一顺序栈s*/
El primer elemento de la pila de convenciones se almacena en data[1], luego:
  • s -> top==0 significa que la pila de secuencias s está vacía
  • s -> top==maxsize-1 significa que la pila secuencial s está llena 

③ Operación básica de pila secuencial

Ⅰ  Inicialización  

【Código de ejemplo】

// 初始化栈
int Initstack(SeqStk* stk)
{
    stk->top = 0; // 将栈的顶部指针设置为0,表示栈为空
    return 1; // 返回1表示初始化成功
}

【Diagrama esquemático】

【Explicación del código】

1. Este código implementa una función para inicializar la pila  Initstack .

2. El parámetro de la función es un puntero a  SeqStk la estructura  stk , a través del cual podemos manipular las propiedades relevantes de la pila.

3. Los comentarios en el código explican lo que hace cada línea:

  • stk->top = 0;top Establezca el puntero superior de la pila en 0, lo que indica que la pila está vacía.
  • return 1; Devuelve 1, lo que indica que la pila se inicializó correctamente.

4. El propósito de esta función es inicializar el puntero superior de la pila a 0 para las operaciones subsiguientes de inserción y extracción.

5. Cabe señalar que la función que devuelve 1 se puede utilizar como una señal de éxito, que se puede modificar adecuadamente según las necesidades reales.

Ⅱ  Juzga que la pila está vacía

【Código de ejemplo】

// 判断栈是否为空
int EmptyStack(SeqStk* stk)
{
    if (stk->top == 0) // 如果栈的顶部指针为0,表示栈为空
        return 1; // 返回1表示栈为空
    else
        return 0; // 返回0表示栈不为空
}

【Explicación del código】

1. Este código implementa una función para determinar si la pila está vacía EmptyStack .

2. El parámetro de la SeqStkfunción stk , a través del cual podemos obtener el puntero superior de la pila  top.

3. Los comentarios en el código explican lo que hace cada línea:

  • if (stk->top == 0) Si el puntero superior de la pila top es 0, significa que la pila está vacía.
  • return 1; Devuelve 1, lo que indica que la pila está vacía.
  • else return 0; Si el puntero superior de la pila top no es 0, significa que la pila no está vacía y devuelve 0.

4. El propósito de esta función es juzgar si la pila está vacía, y el juicio lógico correspondiente se puede hacer de acuerdo con el valor de retorno 0 o 1.

5. Cabe señalar que en el juicio condicional se debe utilizar para  == expresar la relación de igualdad, no un solo  operador de asignación.

6. El valor devuelto es 1 cuando la pila está vacía, de lo contrario, el valor devuelto es 0

Ⅲ  Empuje en la pila

【Código de ejemplo】

// 入栈操作
int Push(SeqStk* stk, DataType x)
{
    /* 数据元素x进顺序栈stk */
    if (stk->top == maxsize - 1) // 判断栈是否上溢(满栈)
    {
        error("栈满"); // 输出错误信息,表示栈已满
        return 0; // 返回0,表示入栈失败
    }
    else
    {
        stk->top++; // 修改栈顶指针,指向新的栈顶
        stk->data[stk->top] = x; // 将元素x插入新的栈顶位置
        return 1; // 返回1,表示入栈成功
    }
}

【Explicación del código】

1. Este código implementa una función de operación de pila  Push.

2. Los parámetros de la SeqStk función incluyen un stkpuntero a la x

3. Los comentarios en el código explican lo que hace cada línea:

  • if (stk->top == maxsize - 1) Determine top si  es igual a maxsize - 1, es decir, si la pila está llena.
  • error("栈满"); Si la pila está llena, muestra el mensaje de error "pila llena".
  • return 0; Devuelve 0, lo que indica que la inserción falló.
  • stk->top++; El puntero superior de la pila top aumenta , apuntando a la nueva posición superior de la pila.
  • stk->data[stk->top] = x; Inserte el elemento x en la nueva posición superior de la pila.
  • return 1; Devuelve 1, lo que indica que la pila se ha realizado correctamente.

4. El propósito de esta función es insertar elementos de datos xen la pila secuencial y generar un mensaje de error si la pila está llena.

5. Un valor de retorno de 0 indica que el apilamiento falló y un valor de retorno de 1 indica que el apilamiento fue exitoso.

 

【Código de ejemplo】

// 出栈操作
int Pop(SeqStk* stk)
{
    /* 顺序栈stk的栈顶元素退栈 */
    if (stk->top == 0) // 判断栈是否下溢(空栈)
    {
        error("栈空"); // 输出错误信息,表示栈已空
        return 0; // 返回0,表示出栈失败
    }
    else
    {
        stk->top--; // 修改栈顶指针,指向新的栈顶
        return 1; // 返回1,表示出栈成功
    }
} /* Pop */

【Explicación del código】

1. Este código implementa una función para abrir la pila  Pop.

2. El parámetro de la función es un  SeqStk puntero a la estructura  stk.

3. Los comentarios en el código explican lo que hace cada línea:

  • if (stk->top == 0) Determine top si es 0, es decir, si la pila está vacía.
  • error("栈空"); Si la pila está vacía, se emite el mensaje de error "pila vacía".
  • return 0; Devuelve 0, lo que indica un error al salir de la pila.
  • stk->top--; El puntero superior de la pila top se reduce para señalar la nueva parte superior de la pila.
  • return 1; Devuelve 1, lo que indica que la pila se extrajo con éxito.

4. El propósito de esta función es desapilar el elemento superior stk de y generar un mensaje de error si la pila está vacía.

5. Un valor de retorno de 0 indica que la ventana emergente falló y un valor de retorno de 1 indica que la ventana emergente fue exitosa.

6. El último /* Pop */ es un comentario para la función, que indica que el segmento de código implementa la operación de pila.

Ⅴ  Tome el elemento superior de la pila

【Código de ejemplo】

// 获取栈顶元素
DataType GetTop(SeqStk* stk)
{
    if (EmptyStack(stk)) // 判断栈是否为空
        return NULLData; // 如果为空,返回空数据元素
    else
        return stk->data[stk->top]; // 返回栈顶元素
}

【Explicación del código】

1. Este código implementa una función para obtener el elemento superior de la pila  GetTop.

2. El parámetro de la función es un puntero a SeqStk la estructura  stk.

3. Los comentarios en el código explican lo que hace cada línea:

  • if (EmptyStack(stk)) Llame EmptyStack a la función para determinar si la pila está vacía.
  • return NULLData; Si la pila está vacía, devuelve un elemento de datos vacío.
  • else return stk->data[stk->top]; Si la pila no está vacía, devuelve el elemento superior de la pila.

4. El propósito de esta función es obtener el elemento superior stk de la pila está vacía, devuelve un elemento de datos vacío.

5. De lo contrario, devuelva el valor del elemento superior de la pila.

6. Cabe señalar que, de acuerdo con la situación real, NULLData se pueden realizar las modificaciones apropiadas según el tipo de datos.


(3) Implementación de enlace de pila - cadena de pila

① Definición de cadena de pila

  • La estructura de almacenamiento vinculada de la pila se denomina pila vinculada . Es una lista de un solo vínculo con operaciones limitadas. Las operaciones de inserción y eliminación solo se limitan a la posición del encabezado.
  • El puntero superior de la pila es el puntero principal de la lista enlazada

 

② Escriba la descripción de la pila encadenada

Tipo de pila de cadena

// 定义链式栈节点结构体
typedef struct node {
    DataType data;           // 数据元素
    struct node* next;       // 指向下一个节点的指针
} LkStk;

【Aviso】

  • Condición de subdesbordamiento: LS -> siguiente == NULL
  • Overflow: no considera el fenómeno stack full

【Explicación del código】

1. Este código define una estructura de nodos de pila encadenados  LkStk.

2. Contiene las siguientes dos variables miembro:

  • DataType data; Una variable que almacena elementos de datos, aquí se supone DataType que es un tipo de datos personalizado.
  • struct node* next; Un puntero al siguiente nodo, utilizado para implementar una estructura de cadena.

3. La estructura representa un nodo en la pila de cadena y cada nodo contiene un elemento de datos y un puntero al siguiente nodo.

4. Mediante la conexión continua de nodos, se puede formar una estructura de pila en cadena.

③ Operación básica de la pila de cadena

Ⅰ  Inicialización  

【Código de ejemplo】

// 初始化链式栈
void InitStack(LkStk* LS)
{
    LS = (LkStk*)malloc(sizeof(LkStk));  // 分配内存空间,LS指向新的栈顶节点
    LS->next = NULL;                     // 将栈顶节点的指针指向NULL,表示栈为空
}

【Explicación del código】

1. Este código implementa una función para inicializar la pila de cadena  InitStack.

2. El parámetro de la función es un puntero a LkStk la estructura  LS.

3. Los comentarios en el código explican lo que hace cada línea:

  • LS = (LkStk*)malloc(sizeof(LkStk)); Utilice  malloc la asignación dinámica de espacio de memoria para apuntar el puntero LSal nuevo nodo superior de la pila.
  • LS->next = NULL;next Asigne  el puntero del nodo superior de la pila a NULL, lo que indica que la pila vinculada está vacía.

4. El propósito de esta función es inicializar la pila de cadena.

5. Mediante la asignación dinámica de espacio de memoria, cree un nodo superior de pila y apunte su puntero  NULLpara indicar que la pila vinculada está vacía.

6.  Cabe señalar que dado que el parámetro es un puntero, la modificación del valor del puntero dentro de la función no afectará el parámetro real que se pasa cuando se llama a la función.

7. Por lo tanto, si desea poder acceder al nodo superior de la pila recién asignado después de llamar a la función, se recomienda cambiar el parámetro de la función a un puntero doble o devolver el puntero del nodo superior de la pila recién asignado.

Ⅱ  Juzga que la pila está vacía

【Código de ejemplo】

// 判断链式栈是否为空
int EmptyStack(LkStk* LS)
{
    if (LS->next == NULL)  // 判断栈顶节点指针是否为空
        return 1;         // 如果为空,返回1表示栈为空
    else
        return 0;         // 如果不为空,返回0表示栈不为空
}

【Explicación del código】

1. Este código implementa una función para determinar si la pila vinculada está vacía  EmptyStack.

2. El parámetro de la LkStk función LSun

3. Los comentarios en el código explican lo que hace cada línea:

  • if (LS->next == NULL) Juzgar next si el puntero del nodo superior de la pila está vacío, es decir, si la pila encadenada está vacía.
  • return 1; Si la pila está vacía, devuelve 1, lo que indica que la pila está vacía.
  • return 0; Si la pila no está vacía, devuelve 0, lo que indica que la pila no está vacía.

4. El propósito de esta función es juzgar si la pila vinculada está vacía.

5. Determine el estado de la pila de la cadena juzgando si el puntero del nodo superior de la pila está vacío.

6.  Si el puntero del nodo superior de la pila está vacío, significa que la pila está vacía, de lo contrario, significa que la pila no está vacía.

7. Un valor de retorno de 1 indica que la pila está vacía y un valor de retorno de 0 indica que la pila no está vacía.

Ⅲ  Empuje en la pila

[Definición] insertar en la pila  insertar un elemento  x en la parte superior de la pila

  • Genere un nuevo nodo (la pila de cadena no se desbordará)
  • Inserte un nuevo nodo en la pila de cadenas y conviértalo en el nuevo nodo superior de la pila

【Código de ejemplo】

// 将元素入栈
void Push(LkStk* LS, DataType x)
{
    LkStk* temp;                           // 定义临时指针变量temp
    temp = (LkStk*)malloc(sizeof(LkStk));  // 分配内存空间,创建新的节点
    temp->data = x;                        // 将输入的元素x赋值给新节点的数据域
    temp->next = LS->next;                 // 将新节点的next指针指向原来栈顶节点的next指针
    LS->next = temp;                       // 将栈顶指针指向新节点,使其成为新的栈顶节点
}

【Diagrama esquemático】

【Explicación del código】

1. Este código implementa una función que empuja elementos a la pila  Push.

2. Los parámetros de la función incluyen un  LkStk puntero a la estructura LS y un DataType elemento de tipo  x, que indica el elemento que se va a colocar en la pila.

3. Los comentarios en el código explican lo que hace cada línea:

  • LkStk* temp; Defina una variable de puntero temporal  temppara apuntar al nodo recién creado.
  • temp = (LkStk*)malloc(sizeof(LkStk)); Utilícelo para asignar malloc dinámicamente espacio de memoria y crear un nuevo nodo.
  • temp->data = x;x Asigne el elemento que se empujará a la pila al campo de datos del nuevo nodo.
  • temp->next = LS->next; Apunte el next puntero al next puntero del nodo superior de la pila original para realizar la conexión de la estructura de la cadena.
  • LS->next = temp; Apunte el puntero superior de la pila LS->next al nuevo nodo, haciendo que el nuevo nodo sea el nuevo nodo superior de la pila.

4. El propósito de esta función es empujar elementos xa la pila.

5. Primero cree un nuevo nodo y asigne elementos al campo de datos del nuevo nodo.

6.  Luego apunte el next puntero al next puntero del nodo superior de la pila original, y luego apunte el puntero de la parte superior de la pila al nuevo nodo para darse cuenta la operación de empuje.

 

[Definición] Emerger  elimina un elemento en la parte superior de la pila y regresa

  • Considere el problema de subdesbordamiento
  • Si no se desborda, saque el elemento superior de la pila, elimine el nodo superior de la pila de cadena y devuelva el nodo al sistema

【Código de ejemplo】

// 将元素出栈
int Pop(LkStk* LS)
{
    LkStk* temp;                            // 定义临时指针变量temp
    if (!EmptyStack(LS))                    // 判断栈是否为空
    {
        temp = LS->next;                    // 将temp指针指向栈顶节点
        LS->next = temp->next;              // 将栈顶指针指向栈顶节点的下一个节点,跳过当前栈顶节点
        free(temp);                         // 释放当前栈顶节点的内存空间
        return 1;                           // 返回1表示出栈成功
    }
    else
        return 0;                           // 返回0表示栈为空,无法进行出栈操作
}

【Diagrama esquemático】

【Explicación del código】

1. Este código implementa una función que extrae elementos de la pila  Pop.

2. El parámetro de la función es un  LkStk puntero a una estructura  LS, que se utiliza para representar la pila de cadenas que se operará.

3. Los comentarios en el código explican lo que hace cada línea:

  • LkStk* temp; Defina una variable de puntero temporal  temppara apuntar al nodo superior de la pila actual.
  • if (!EmptyStack(LS)) Para juzgar si la pila está vacía, use  EmptyStack() la función para ayudar en el juicio.
  • temp = LS->next; Apunte la variable de puntero temporal temp al nodo superior de la pila.
  • LS->next = temp->next; Apunte el puntero superior de la pila al siguiente nodo del nodo superior de la pila, omitiendo el nodo superior de la pila actual.
  • free(temp); Libere el espacio de memoria del nodo superior de la pila actual, es decir, elimínelo de la pila de la cadena.
  • return 1; Devolver 1 indica que la pila se abrió con éxito.
  • return 0; Si la pila de enlaces está vacía, devolver 0 significa que la pila está vacía y no se puede abrir.

4. El propósito de esta función es realizar la operación de apilamiento de elementos.

5. Primero juzgue si la pila está vacía, si no lo está, apunte el puntero superior de la pila al siguiente nodo del nodo superior de la pila y libere el espacio de memoria del nodo superior actual de la pila en el Mismo tiempo.

6. Si la pila está vacía, significa que no se puede realizar la operación de pila.

7.  Un valor de retorno de 1 significa que la ventana emergente se realizó correctamente y un valor de retorno de 0 significa que la pila está vacía y no se puede abrir.

Ⅴ  Tome el elemento superior de la pila

【Código de ejemplo】

// 获取栈顶元素
DataType GetTop(LkStk* LS)
{ 
    if (!EmptyStack(LS))                    // 判断栈是否为空
        return LS->next->data;              // 返回栈顶节点的数据域值
    else 
        return NULLData;                    // 如果栈为空,则返回空数据(NULLData)
}

【Explicación del código】

1. Este código implementa una función para obtener el elemento superior de la pila  GetTop.

2. El parámetro de la función es un  LkStk puntero a una estructura  LS, que se utiliza para representar la pila de cadenas que se operará.

3. Los comentarios en el código explican lo que hace cada línea:

  • if (!EmptyStack(LS)) Para juzgar si la pila está vacía, use EmptyStack() la función para ayudar en el juicio.
  • return LS->next->data; Si la pila no está vacía, devuelve el valor del campo de datos del nodo superior de la pila.
  • return NULLData; Si la pila está vacía, devuelve datos vacíos ( NULLData), lo que significa que no hay elementos en la pila.

4. El propósito de esta función es obtener el valor del elemento superior de la pila sin modificar la pila.

5. Primero juzgue si la pila está vacía, si no, devuelva el valor del campo de datos del nodo superior de la pila, si la pila está vacía, devuelva datos vacíos ( ) NULLDatapara indicar que no hay elementos en la pila.


(4) Aplicación y recursividad de la pila.

① Ejemplos básicos de aplicación de la pila

[Ejemplo 1]  Supongamos que la secuencia de entrada de la pila es 1, 2, 3, 4 en secuencia, ¿cuál de las secuencias de salida obtenidas coincide?

【Análisis del problema】 Respuesta correcta: d

  • d 3,4,2,1La pila es una estructura de datos que sigue el principio "Último en entrar, primero en salir" (Last In First Out, LIFO), por lo que cuando insertamos los elementos 1, 2, 3 y 4 en la pila en secuencia, la secuencia de salida resultante solo puede ser 
  • Ninguna de las otras opciones ( a 1,2,3,4 ; b 4,2,3,1 ; c 1,3,2,4 ) puede ser la secuencia de salida de la pila.
  • La opción d 3,4,2,1 es la única secuencia de salida que se ajusta a las propiedades de la pila y corresponde al caso en que la secuencia de entrada es 1,2,3,4.

[Respuesta detallada]

  • Opción a 1,2,3,4 : la pila generará elementos de acuerdo con el principio de último en entrar, primero en salir, por lo que la secuencia de salida será 4,3,2,1, que no coincide con las opciones dadas.
  • Opción b 4,2,3,1 : la pila generará primero el último elemento insertado en la pila, por lo que la secuencia de salida será 4,3,2,1, que no coincide con la opción dada.
  • Opción c 1,3,2,4 : antes de que la pila genere el elemento 4, primero debe generar el elemento 3, y la secuencia dada por la opción c es 1,3, lo cual es inconsistente con las características de la pila.
  • Opción d 3,4,2,1 : la pila es una estructura de datos de último en entrar, primero en salir (LIFO), y el orden en que los elementos se extraen de la pila es opuesto al orden en que se insertaron la pila. Según la secuencia de entrada en el título es 1, 2, 3, 4, según las características de la pila, el elemento 4 se coloca en la pila primero, luego el elemento 3, luego el elemento 2 y finalmente el elemento 1. Por lo tanto, el orden en la pila es 4,3,2,1. La secuencia de salida 3, 4, 2, 1 correspondiente a la opción d también es consistente con las características de la pila.Primero se extrae el elemento 3, luego el elemento 4, luego el elemento 2 y el último elemento 1. Entonces, la opción d es la secuencia de salida correcta que se ajusta a las reglas de la pila y es consistente con la secuencia de entrada.

[Ejemplo 2] Operación de pila secuencial

【Código de ejemplo】

const int maxsize = 50;                            // 定义常量 maxsize,表示栈的最大大小
typedef struct seqstack                             // 定义结构体 seqstack,表示顺序栈
{
    char data[maxsize];                             // 字符数组,用于存储栈中的元素
    int top;                                        // 整型变量,表示栈顶指针的位置
} seqstk;

int main()
{
    seqstk stk;                                     // 声明一个 seqstk 类型的变量 stk
    int i;
    char ch;

    Initstack(&stk);                                // 初始化栈 stk

    for (ch = 'A'; ch <= 'A' + 10; ch++)             // 将字符 'A' 到 'A' + 10 入栈
    {
        Push(&stk, ch);                             // 将字符 ch 压入栈 stk
        printf("%c", ch);                           // 打印字符 ch
    }
    printf("\n");

    while (!Emptystack(&stk))                       // 当栈不为空时执行循环
    {
        ch = GetTop(&stk);                           // 获取栈顶元素赋值给变量 ch
        Pop(&stk);                                  // 弹出栈顶元素
        printf("%c", ch);                           // 打印变量 ch 的值
    }
    printf("\n");

    return 0;
}

【Explicación del código】

1. La función del código es crear una pila secuencial e inicializarla, luego empujar los caracteres 'A' a 'K' en la pila en secuencia, luego imprimir los caracteres empujados a la pila y finalmente abrir la pila e imprimir los personajes fuera de la pila a su vez.

2. Los detalles son los siguientes:

  • const int maxsize = 50; Defina una constante  maxsizeque represente el tamaño máximo de la pila.
  • typedef struct seqstack Defina un nuevo tipo de datos  seqstkque represente una pila secuencial.
  • char data[maxsize]; Defina una matriz de caracteres en  seqstk la estructura  datapara almacenar los elementos en la pila.
  • int top; Defina una variable entera en  seqstk la estructura  toppara indicar la posición del puntero superior de la pila.
  • int main() Entrada de la función principal.
  • seqstk stk; Declare una  seqstk variable de tipo  stkpara representar una pila secuencial.
  • Initstack(&stk); Llame a la función  Initstack() para inicializar la pila  stk.
  • for (ch = 'A'; ch <= 'A' + 10; ch++) Bucle, empuje los caracteres 'A' a 'A' + 10 en la pila en secuencia.
  • Push(&stk, ch); La función de llamada  empuja Push() los caracteres  ch a la pila  stk.
  • printf("%c", ch); Imprimir caracteres  ch.
  • while (!Emptystack(&stk)) Ejecute el bucle mientras la pila no esté vacía.
  • ch = GetTop(&stk); Llame a la función  GetTop() para obtener el elemento superior de la pila y asígnelo a una variable  ch.
  • Pop(&stk); La función de llamada  Pop() saca el elemento superior de la pila.
  • printf("%c", ch); Imprime  ch el valor de una variable.
  • return 0; Devuelve el indicador de que la función principal se ejecuta correctamente.

3. En resumen, la función de este código es crear una pila secuencial, insertar una serie de caracteres en la pila e imprimirlos uno por uno, y luego sacar los elementos de la pila uno por uno e imprimirlos. y el resultado de salida final es "ABCDEFGHIJK".

【Resultados del】 

  • Durante la ejecución, los caracteres 'A' a 'K' se colocan en la pila, por lo que la salida de la primera línea es "ABCDEFGHIJK".
  • Luego, en cada bucle, el elemento superior de la pila se extrae y se imprime hasta que la pila está vacía.
  • Por lo tanto, la segunda línea de salida es "KJIHGFEDCBA", que es el orden inverso de los caracteres 'K' a 'A'.
ABCDEFGHIJK
KJIHGFEDCBA

[Ejemplo 3] Escriba un algoritmo que invierta la lista enlazada individualmente con el nodo principal que se muestra en la siguiente figura con la ayuda de una pila.

【Código de ejemplo】

void ReverseList(LkStk *head)
{
    Lkstk *S;                       // 定义指向链栈的指针变量 S
    DataType x;                     // 定义数据类型变量 x,用于临时存储链表节点的数据
    InitStack(S);                   // 初始化链栈 S
    p = head->next;                 // p 指向链表的第一个节点

    while (p != NULL)               // 扫描链表
    {
        Push(s, p->data);           // 元素进栈:将链表节点的数据入栈
        p = p->next;                // p 指向下一个节点
    }

    p = head->next;                 // 重新让 p 指向链表的第一个节点

    while (!EmptyStack(S))          // 栈不为空时
    {
        p->data = GetTop(S);        // 元素填入单链表中:将栈顶元素赋值给链表节点的数据
        Pop(S);                     // 出栈:弹出栈顶元素
        p = p->next;                // p 指向下一个节点
    }
}

【Explicación del código】

1. La función de este código es realizar la operación inversa de la lista enlazada. Los pasos específicos de implementación son los siguientes:

  1. Declare una variable de puntero que apunte a la pila de enlaces  S.
  2. Declare una variable temporal  xpara almacenar los datos de los nodos de la lista enlazada.
  3. Inicialice la pila de cadenas  S.
  4. Apunte el puntero  p al primer nodo de la lista enlazada.
  5. Recorra la lista enlazada e inserte los datos del nodo en la pila.
  6. Vuelva a apuntar el puntero  p al primer nodo de la lista vinculada.
  7. Recorra la pila, asigne el elemento superior de la pila a los datos del nodo de la lista vinculada a su vez y extraiga el elemento superior de la pila.
  8. Apunte el puntero  p al siguiente nodo, es decir, recorra la lista enlazada hacia atrás.
  9. Complete la operación inversa de la lista enlazada.

2. Los detalles son los siguientes:

  • void ReverseList(LkStk *head) El nombre de la función es  ReverseListy el parámetro es un puntero al nodo principal de la lista vinculada  head, lo que indica que la lista vinculada está invertida.
  • Lkstk *S; Declare un  Lkstk tipo de variable de puntero  Sque represente el puntero de la pila de enlaces.
  • DataType x;DataType Declare una variable   cuyo tipo de datos  xes para almacenar temporalmente los datos de los nodos de la lista enlazada.
  • InitStack(S); Llame a la función  para  inicializar InitStack() la pila de enlaces  .S
  • p = head->next; Apunte el puntero  p al primer nodo de la lista enlazada.
  • while (p != NULL) Ejecute el bucle mientras el puntero  p no sea nulo.
  • Push(s, p->data); Llame a la función  Push() para insertar los datos del nodo de la lista vinculada en la pila.
  • p = p->next; Apunte el puntero  p al siguiente nodo de la lista vinculada, es decir, recorra la lista vinculada hacia atrás.
  • p = head->next; Vuelva a apuntar el puntero  p al primer nodo de la lista vinculada.
  • while (!EmptyStack(S)) Ejecute el bucle mientras la pila de enlaces  S no esté vacía.
  • p->data = GetTop(S); Asigne el elemento superior de la pila a los datos del nodo de la lista vinculada.
  • Pop(S); La función de llamada  Pop() saca el elemento superior de la pila.
  • p = p->next; Apunte el puntero  p al siguiente nodo de la lista vinculada, es decir, recorra la lista vinculada hacia atrás.

3. En resumen, esta función logra la inversión de la lista vinculada mediante el uso de una pila vinculada y variables temporales para insertar los datos de los nodos de la lista vinculada en la pila primero y luego sacarlos de la pila.

② Definición de recursividad

Si una función se llama a sí misma antes de completarse, se llama función recursiva .

[Ejemplo] Encuentra la función factorial de un número entero  :

【Código de ejemplo】

int f(int n)
{
    if (n == 0)                         // 如果 n 等于 0
        return 1;                       // 返回 1
    else                                // 否则
        return n * f(n - 1);            // 递归调用函数 f,传入 n-1,并将结果乘以 n 返回
}

【Explicación del código】

  • int f(int n) El nombre de la función es  fy el parámetro es un número entero  n, lo que indica la función para calcular el factorial.
  • if (n == 0) Si n es igual a 0, significa que se recurrió al caso más básico, y el resultado del factorial es 1.
  • return 1; Devuelve el resultado 1.
  • else Si n no es igual a 0, significa que se debe continuar con el cálculo recursivo del factorial.
  • return n * f(n - 1); Llame a la función f recursivamente, pasando n-1, y devuelva el resultado multiplicado por n. El propósito de la llamada recursiva es calcular el factorial de n igual a n veces el factorial de n-1.

③ Forma general de recursividad

void fname(参数表)
{
    if (数据作为递归出口)
    {
        // 简单操作
    }
    else
    {
        // 简单操作
        fname(参数表);
        // 简单操作
        // [fname(参数表); 简单操作;]
        // 可能有多次递归调用
    }
}

[Explicación detallada] Lo anterior es la forma general de una función recursiva, y la explicación específica es la siguiente:

  • void fname(参数表): El nombre de la función es  fname, con un conjunto de parámetros.
  • if (数据作为递归出口): Cuando se cumple una determinada condición, los datos se utilizan como salida de la recursión, es decir, la recursión finaliza.
  • // 简单操作: Acción simple realizada en la salida recursiva.
  • else: Si no se cumple la condición, realice las siguientes operaciones.
  • // 简单操作: Operaciones simples realizadas antes o después de la llamada recursiva.
  • fname(参数表): Llame a la función recursivamente  fnamey pase la lista de parámetros.
  • // 可能有多次递归调用: Después de la llamada recursiva, la función puede continuar  fname siendo llamada para la recursividad y puede haber múltiples llamadas recursivas.

[Nota] La función recursiva debe cumplir las condiciones para el final de la recursión; de lo contrario, ingresará a una recursividad infinita y hará que el programa se bloquee. Al mismo tiempo, las funciones recursivas deben tener en cuenta el orden de las llamadas recursivas y el establecimiento de las condiciones de finalización para garantizar que la recursividad pueda finalizar normalmente.



Dos, la cola

Los archivos de encabezado que describen las implementaciones de pila y cola son los siguientes:

  • Seqstack.h: La definición e implementación de la pila secuencial.
  • Lkstack.h: Definición e implementación de cadena de pila.
  • Sequeue.h: Definición de cola de secuencia y su implementación.
  • Lkqueue.h: La definición e implementación de la cola en cadena. 

(1) El concepto básico de cola

① Definición

Queue: También es una tabla lineal con operaciones limitadas.
  • Queue  : es una tabla lineal que solo permite inserciones en un extremo de la tabla y eliminaciones en el otro extremo.
  • El extremo que permite la eliminación se llama frente .
  • El otro extremo que permite la inserción se llama la cola de la cola (posterior)
  • Cola Q=(a1, a2, a3,...an)

② Diagrama esquemático

 

【Ejemplo】  Compras en línea

  • Cola de trabajos en el sistema operativo
  • El miembro que entra primero en la cola siempre sale primero de la cola
  • Por lo tanto, la cola también se denomina tabla lineal de primero en entrar, primero en salir (First In First Out) , o  tabla FIFO  para abreviar .
  • Cuando no hay elementos en la cola, se denomina cola vacía.
  • Después de agregar los elementos  a 1 , a 2 , ... an a la cola vacía en secuencia , a es el elemento principal de la cola y a es  el elemento final de la cola.
  • Obviamente, el orden de salida  de la cola sólo puede ser un 1 , un 2 , ... un n , es decir, la modificación de la cola se realiza según el principio de first in first out .

③ Características de la cola

Primero en entrar, primero en salir ( FIFO ) :

El propósito de la cola: a menudo se usa para almacenar temporalmente datos para ser procesados.

④ Funcionamiento básico de la cola

➢Inicialización de cola InitQueue(Q):
  • Establecer una cola vacía  Q
➢ColaVacía ( Q):
  • Si la cola  está vacía, el valor devuelto es  1 , de lo contrario, el valor devuelto es  0
➢Poner en cola Poner en cola (Q,x):
  • Inserte el elemento de datos  en la cola desde el final de la cola, convirtiéndolo en el nuevo elemento de cola de la cola
➢Cola de salida (Q):
  • eliminar el primer elemento de la cola
➢Obtener el primer elemento de la cola GetHead(Q):
  • Devuelve el valor del primer elemento de la cola.

(2) Implementación secuencial de colas

① cola secuencial

[Definición] Cola secuencial solo se puede insertar desde un extremo de la matriz y eliminar desde el otro extremo.
[Descripción] SeqQue sq ;
  • Condición de desbordamiento: sq.rear == maxsize-1 (cola llena)
  • Condición de subdesbordamiento: sq.rear == sq.front (cola vacía)
[Nota] Desbordamiento falso : sq.rear == maxsize-1 , pero la capacidad real en la cola no ha alcanzado la capacidad máxima.

② Cola circular

[Definición] La implementación secuencial de la cola -  cola circular
  • Use una matriz unidimensional como estructura de almacenamiento de la cola
  • Capacidad de la cola : el número máximo de elementos que se pueden almacenar en la cola
【Diagrama esquemático】

 

【Acuerdo】 

  • Inicial: delantero=trasero=0
  • Enqueue: la parte trasera se incrementa en 1, y el elemento se inserta en la posición señalada por el puntero de la cola
  • Eliminación de la cola: aumente el frente en 1, obtenga el elemento en la posición señalada por el puntero de la cabeza

【ilustrar】 

  • El frente del puntero de la cabeza:  siempre apunta a la posición anterior del elemento de la cabeza real
  • El indicador de cola trasero -  siempre apunta al elemento de cola real

➢En  la operación de cola:
  • cuadrado.trasero=cuadrado.trasero+1;
  • sq.data[sq.trasero]=x;
➢Operación de eliminación de cola  :
  • frente cuadrado=frente cuadrado+1;

  1. La figura a es una cola vacía, sq.rear=0, sq.front=0.
  2. La figura b muestra que después de 20 ingrese a la cola, sq.rear=1, sq.front=0.
  3. La figura c muestra que después de que 30, 40 y 50 se ponen en cola por turnos, sq.rear=4, sq.front=0.
  4. La figura d muestra que después de que 20, 30, 40 y 50 estén fuera de la cola en secuencia, sq.rear=4, sq.front=4.
  5. La figura e muestra que después de 60 ingrese a la cola, sq.rear=5, sq.front=4.
【Código de ejemplo】 
const int maxsize = 20;  // 定义队列的最大容量为20

// 定义顺序队列结构体
typedef struct seqqueue
{ 
    DataType data[maxsize];  // 存储队列中的元素
    int front, rear;  // 队头指针和队尾指针
} SeqQue;

SeqQue sq;  // 声明一个顺序队列变量sq
【Explicación del código】 

1. La siguiente es una explicación de comentario para cada línea de código:

  • const int maxsize = 20; Se define una constante  maxsizeque indica que la capacidad máxima de la cola es 20.
  • typedef struct seqqueue define un  seqqueue tipo de estructura llamado .
  • DataType data[maxsize]; seqqueue Se define una matriz en la estructura  para  dataalmacenar los elementos en la cola. El tamaño de la matriz es  maxsize.
  • int front, rear; En  seqqueue la estructura  se definen dos variables enteras front y  rear, que representan respectivamente el puntero de cabeza de cola y el puntero de cola de cola.
  • SeqQue sq; Se declara una variable de cola secuencial  sq, de tipo  SeqQue.

2. Este código define una estructura de datos de cola secuencial  SeqQue, que contiene una matriz de tamaño fijo  data y dos punteros que indican la posición de la cabeza y la cola de la  front cola  rear.

3. Al declarar  sq variables, podemos usar  SeqQue estructuras para crear objetos de cola secuenciales.

Ⅰ Definición de cola circular 

[Definición] Cola circular :  asigne un espacio de almacenamiento ( representación de matriz ) para la cola y considere este espacio de almacenamiento como conectado de cabeza a cola.
【Diagrama esquemático】
【Aviso】
  • Puntero de cabeza  frontal  una posición detrás del elemento de cabeza de cola real en el sentido de las agujas del reloj
  • Puntero de cola  trasero apunta al elemento de cola real

Ⅱ Implementación de Circular Queue Loop 

➢Para insertar en la cola: el puntero al final de la cola se incrementa en  1
Sq.rear=(sq.rear+1)%maxsize

Dequeue inmediatamente después de la eliminación: el puntero de la cabeza de la cola se incrementa en  1

Sq.front=(sq.front+1)%maxsize

Ⅲ Definición de tipo de cola circular 

【Código de ejemplo】

typedef struct Cycqueue
{
    DataType data[maxsize];     // 存储数据的数组
    int front, rear;            // 队头和队尾指针
} CycQue;                       // 定义循环队列类型

CycQue CQ;                      // 声明一个循环队列实例 CQ

【Explicación del código】

  • typedef struct Cycqueue Indica la definición de un tipo de estructura  Cycqueue.
  • {} Defina los miembros de la estructura dentro de llaves.
  • DataType data[maxsize]; Declare una  data matriz nombrada para almacenar datos, el tamaño de la matriz es  maxsize.
  • int front, rear; Declare dos variables enteras  front y  rear, que representan los punteros de cabeza y cola de la cola circular respectivamente.
  • } CycQue; Indica el final de la definición del tipo de estructura y le cambia el nombre a  CycQue.
  • CycQue CQ; Declare una instancia de cola circular  CQ, utilizando el tipo de estructura definido anteriormente.

Ⅳ Provisiones 

cola circular  CQ
  • La condición de subdesbordamiento significa que la cola está vacía : CQ.front==CQ.rear
  • La condición de desbordamiento es que la cola está llena : el puntero de la cola alcanza al puntero de la cabeza por detrás
Es decir: (CQ.rear+1)%maxsize==CQ.front ( cabeza de captura de cola )
  • Se desperdicia un espacio, cuando el equipo está lleno, la capacidad real del equipo  = maxsize-1

③ Operación básica de cola circular

Ⅰ  Inicialización  

【Código de ejemplo】

void InitQueue(CycQue CQ)
{
    CQ.front = 0;           // 初始化循环队列的队头指针为 0
    CQ.rear = 0;            // 初始化循环队列的队尾指针为 0
}

【Explicación del código】

1. Esta función se utiliza para inicializar la cola circular y establecer los punteros de cabeza y cola de cola en 0, lo que indica que la cola está vacía.

2. La explicación detallada es la siguiente:

  • void InitQueue(CycQue CQ) Indica que una función está definida  InitQueuey el parámetro es una cola circular  CQ.
  • CQ.front = 0; Inicialice el puntero principal de la cola circular  front en 0, lo que indica que la cola está vacía.
  • CQ.rear = 0; Inicialice el puntero de la cola de la cola circular  rear en 0, lo que indica que la cola está vacía.

Ⅱ  Juzgar que la cola está vacía

【Código de ejemplo】

int EmptyQueue(CycQue CQ)
{
    if (CQ.rear == CQ.front)    // 如果队尾指针等于队头指针,表示队列为空
        return 1;               // 返回 1,表示队列为空
    else
        return 0;               // 返回 0,表示队列不为空
}

【Explicación del código】

1. Esta función se utiliza para juzgar si la cola circular está vacía.

2. Cuando el puntero de cola de cola es igual al puntero de cabeza de cola, significa que la cola está vacía, de lo contrario, significa que la cola no está vacía.

3.  Un valor de retorno de 1 indica que la cola está vacía y un valor de retorno de 0 indica que la cola no está vacía.

4. La explicación detallada es la siguiente:

  • int EmptyQueue(CycQue CQ) Indica que una función está definida  EmptyQueuey el parámetro es una cola circular  CQ.
  • if (CQ.rear == CQ.front) Determine si el puntero de cola de cola  rear es igual al puntero de cabeza de cola  front.
  • return 1; Si el puntero de cola de cola es igual al puntero de cabeza de cola, lo que indica que la cola está vacía, devuelve 1.
  • return 0; Si el puntero de cola de cola no es igual al puntero de cabeza de cola, significa que la cola no está vacía y devuelve 0.

Ⅲ  Entrar en la cola

[Definición] Entrar en el equipo  insertar un nuevo elemento  x al final del equipo

  • ¿Desbordamiento de juicio? Sí, el desbordamiento regresa;
  • De lo contrario, modifique el puntero de la cola de la cola (aumente 1) e inserte el nuevo elemento x en la cola de la cola.

【Código de ejemplo】

// 在循环队列中插入元素
int EnQueue(CycQue CQ, DataType x)
{
    // 判断队列是否已满
    if ((CQ.rear + 1) % maxsize == CQ.front)
    {
        error("队列满");  // 队列满时报错
        return 0;
    }
    else
    {
        CQ.rear = (CQ.rear + 1) % maxsize;  // 队尾指针向后移动一个位置
        CQ.data[CQ.rear] = x;  // 将元素x插入队尾
        return 1;
    }
}

【Explicación del código】

1. Este código implementa la operación de insertar elementos en la cola circular.

2. La explicación detallada es la siguiente:

  • CycQue Es el tipo de estructura de la cola circular.
  • DataType es el tipo de datos de los elementos en la cola.
  • maxsize es la capacidad máxima de la cola.

3. En la función, primero juzgamos si la cola está llena, lo cual se juzga según las siguientes condiciones: (CQ.rear + 1) % maxsize == CQ.front. Si la condición es verdadera, la cola está llena, se generará un mensaje de error y se devolverá 0.

4. Si la cola no está llena,  else se ejecutarán las siguientes sentencias. Primero, mueva el puntero de la cola hacia atrás una posición: CQ.rear = (CQ.rear + 1) % maxsize. Luego, inserte el elemento  x al final de la cola: CQ.data[CQ.rear] = x. Finalmente, la función devuelve 1 para indicar una inserción exitosa.

5. Tenga en cuenta que CQ los parámetros se pasan por valor, por lo que modificar los parámetros dentro de la función  noCQ.rear  afectará el exterior. CQ.dataSi desea modificar una estructura externa dentro de una función, debe usar punteros o referencias como parámetros de función.

Ⅳ  Desencolar

[Definición] Dequeue  elimina el elemento al principio de la cola y regresa

  • ¿Se considera desbordamiento? Sí, desbordamiento de retorno;
  • Si no se desborda, modifique el puntero del encabezado de la cola y obtenga el elemento del encabezado de la cola.

【Código de ejemplo】

// 从循环队列中取出元素
int OutQueue(CycQue CQ)
{
    // 判断队列是否为空
    if (EmptyQueue(CQ))
    {
        error("队列空"); // 队列空时报错
        return 0;
    }
    else
    {
        CQ.front = (CQ.front + 1) % maxsize; // 队头指针向后移动一个位置
        return 1;
    }
}

【Explicación del código】

1. La función del código es sacar elementos de la cola circular.

2. La explicación detallada es la siguiente:

  • CycQue Es el tipo de estructura de la cola circular.
  • maxsize es la capacidad máxima de la cola.

3. En la función, primero juzgamos si la cola está vacía, lo que se  EmptyQueue(CQ) juzga llamando a la función. Si la cola está vacía, se emite un mensaje de error y se devuelve 0.

4. Si la cola no está vacía, se ejecutará la else siguiente . Primero, mueva el puntero de cabeza de línea hacia atrás una posición: CQ.front = (CQ.front + 1) % maxsize. Luego, la función devuelve 1 para indicar que el elemento se recuperó correctamente.

5. Tenga en cuenta que, al igual que las  EnQueue() funciones anteriores, CQ los parámetros  también se pasan por valor, por lo que modificar el interior de la función noCQ.front afectará el exterior. Si desea modificar una estructura externa dentro de una función, debe usar punteros o referencias como parámetros de función.

Ⅴ  Obtener el primer elemento de la cola

【Código de ejemplo】

// 获取栈顶元素
DataType GetTop(LkStk* LS)
{ 
    if (!EmptyStack(LS))                    // 判断栈是否为空
        return LS->next->data;              // 返回栈顶节点的数据域值
    else 
        return NULLData;                    // 如果栈为空,则返回空数据(NULLData)
}

【Explicación del código】

1. La función del código es obtener el elemento cabeza de la cola circular.

2. La explicación detallada es la siguiente:

  • DataType es el tipo de datos de los elementos en la cola.
  • CycQue Es el tipo de estructura de la cola circular.
  • maxsize es la capacidad máxima de la cola.
  • NULLData es un valor específico que representa datos vacíos.

3. En la función, primero juzgamos si la cola está vacía, lo que se  EmptyQueue(CQ) juzga llamando a la función. Si la cola está vacía, devuelve datos vacíos directamente  NULLData.

4. Si la cola no está vacía, ejecute  else la siguiente declaración. Se utiliza aquí  (CQ.front + 1) % maxsize para calcular la siguiente posición de la cabeza de la cola y devolver el elemento en esa posición  CQ.data[(CQ.front + 1) % maxsize].

5. Tenga en cuenta que, al igual que las funciones anteriores,  CQ los parámetros aquí también se pasan por valor, por lo que las modificaciones dentro de la función  CQ.front no afectarán el exterior. Si desea modificar una estructura externa dentro de una función, debe usar punteros o referencias como parámetros de función. Además, defina e inicialice en el código de acuerdo con la situación real  NULLData.


(3) Implementación de enlace de la cola

① Definición de cola en cadena 

[Definición] Cola encadenada : una cola representada por una lista enlazada, es decir, es una lista con un solo enlace que se limita a la eliminación en el encabezado y la inserción al final de la lista .
【Diagrama esquemático】
[Explicación] Obviamente, solo el puntero de cabeza de la lista enlazada única no es conveniente para insertar al final de la lista. Por esta razón, agregue un puntero de cola para apuntar al último nodo de la lista enlazada.
[Nota] Hay dos punteros adjuntos:
  • Puntero de cabecera  frontal  apunta al nodo de cabecera de la tabla; el nodo del elemento de cabecera de la cola está  delante->siguiente
  • Tail pointer rear  apunta al último nodo de la lista enlazada (es decir, el nodo final de la cola)

② Descripción del tipo de cola de cadena 

[Descripción] Los dos punteros de cola de cola y cabeza de cola se pueden encapsular juntos, y el tipo de cola de cadena se puede definir como una estructura
tipo
【Código de ejemplo】
// 定义链式队列结点
typedef struct LinkQueueNode {
    DataType data;               // 结点数据
    struct LinkQueueNode* next;  // 指向下一个结点的指针
} LkQueNode;

// 定义链式队列
typedef struct LkQueue {
    LkQueue* front;  // 队头指针
    LkQueue* rear;   // 队尾指针
} LkQue;

LkQue LQ;

【Explicación del código】

1. El código define el tipo de estructura de la cola encadenada y crea una instancia de cola.

2. La siguiente es una explicación del código:

  • DataType es el tipo de datos de los elementos en la cola.

3. Defina el tipo de estructura del nodo de cola de cadena  LinkQueueNode:

  • data son los datos almacenados en el nodo.
  • next Un puntero al siguiente nodo.

4. Defina el tipo de estructura de la cola encadenada  LkQueue:

  • front Es el puntero de cabecera de la cola, que apunta al primer nodo de la cola.
  • rear Es el puntero de la cola de la cola, que apunta al último nodo de la cola.

5.CreóLkQue LQ;  una instancia de una cola encadenada  LQpara operaciones relacionadas con la cola encadenada.

Tenga en cuenta que las palabras clave se utilizan en el código anterior  typedef para definir nuevos nombres de tipo  LkQueNode y  LkQuepara su uso posterior.

【Aviso】
  • LQ.front -  el puntero de cabeza del equipo encadenado
  • LQ.rear -  el puntero de la cola del equipo encadenado
  1. Desbordamiento de equipos encadenados : se puede ignorar (debido al espacio dinámico de la aplicación)
  2. Desbordamiento del equipo encadenado : es decir, cuando el equipo encadenado está vacío, también se requiere abandonar el equipo;
➢En este momento, no hay ningún nodo real en la lista enlazada:

Provisiones:
  • Cuando la cola de la cadena esté vacía, haga que el  puntero posterior también apunte al nodo de encabezado.
➢Condiciones de desbordamiento de la cola de la cadena :
  • LQ.front->siguiente==NULL
  • 或:LQ. frente==LQ. trasero;

③ Operación básica de cola de cadena  

Ⅰ  Inicialización  

【Código de ejemplo】

// 初始化链式队列
void initQueue(LkQue* LQ)
{ 
    LkQueNode* temp; // 创建一个临时结点指针

    // 分配内存以存储临时结点
    temp = (LkQueNode*)malloc(sizeof(LkQueNode));

    LQ->front = temp;  // 将队头指针指向临时结点
    LQ->rear = temp;   // 将队尾指针指向临时结点

    (LQ->front)->next = NULL;  // 设置临时结点的下一个结点为空
}

【Diagrama esquemático】

【Explicación del código】

1. La función del código es inicializar la cola encadenada.

2. La siguiente es una explicación de comentario para cada línea de código:

  • LkQue* LQ: el parámetro de puntero de cola encadenado entrante, que se utiliza para operar colas relacionadas.
  • LkQueNode* temp: crea un puntero de nodo de cola encadenado temporal.

3. En la función, primero usamos  malloc la función para asignar memoria para el nodo temporal  temp para almacenar el nodo de cola de cadena. sizeof(LkQueNode) Se utiliza para especificar el tamaño de la memoria que se asignará.

Luego, tanto el puntero de cabeza de cola  LQ->front como el puntero de cola de cola  LQ->rear se configuran para apuntar al nodo temporal  temp, de modo que inicialmente solo hay este nodo en la cola.

4.(LQ->front)->next Finalmente,  establezca  el siguiente puntero de nodo del nodo temporal  en NULL, lo que indica que este nodo no tiene un nodo sucesor.

5. De esta forma, se completa la operación de inicialización de la cola.

Ⅱ  Juzgar que la cola está vacía

【Código de ejemplo】

// 判断链式队列是否为空
int EmptyQueue(LkQue LQ)
{
    if (LQ.rear == LQ.front)  // 如果队尾指针和队头指针相同,则队列为空
        return 1;  // 返回1表示队列为空
    else
        return 0;  // 返回0表示队列非空
}

【Explicación del código】

1. La función del código es juzgar si la cola encadenada está vacía.

2. La siguiente es una explicación de comentario para cada línea de código:

  • int: el tipo de retorno de la función es un número entero, lo que indica si la cola está vacía.
  • LkQue LQ: el parámetro de estructura de la cola encadenada entrante, que se utiliza para determinar si la cola está vacía.

3. En la función, primero determinamos si la cola está   vacía al juzgar LQ.rear si el puntero de cola de cola es el mismo que el puntero de cabeza de cola  . LQ.frontSi es lo mismo, la cola está vacía porque no hay elementos en la cola.

4. Si el puntero de cola de cola y el puntero de cabeza de cola no son iguales, significa que la cola no está vacía.

5. Finalmente, la función devuelve 1 para indicar que la cola está vacía y devuelve 0 para indicar que la cola no está vacía.

6. De esta forma, se completa la operación de juzgar si la cola está vacía.

Ⅲ  Entrar en la cola

[Definición] Entrar en el equipo  insertar un elemento  x al final del equipo, es decir, al final de la lista enlazada

  • Genere un nuevo nodo p (establezca su campo de datos en x y su campo de cadena en NULL)
  • Inserte el nuevo nodo p al final de la tabla y conviértase en el nuevo nodo final de la cola

【Diagrama esquemático】

【Código de ejemplo】

// 入队操作
void EnQueue(LkQue* LQ, DataType x)
{ 
    LkQueNode* temp; // 创建一个临时结点指针

    // 分配内存以存储新结点
    temp = (LkQueNode*)malloc(sizeof(LkQueNode));
    
    temp->data = x;  // 设置新结点的数据为x
    temp->next = NULL;  // 设置新结点的下一个结点为空

    (LQ->rear)->next = temp;  // 将新结点添加到队尾结点的后面
    LQ->rear = temp;  // 更新队尾指针为新结点
}

【Explicación del código】

1. La función del código es realizar la operación de puesta en cola de la cola en cadena.

2. La siguiente es una explicación de comentario para cada línea de código:

  • LkQue* LQ: el parámetro de puntero de cola encadenado entrante, que se utiliza para operar colas relacionadas.
  • DataType x: poner en cola los datos.

3. En la función, primero usamos  malloc la función para asignar memoria para el nuevo nodo  temp para almacenar datos. sizeof(LkQueNode) Se utiliza para especificar el tamaño de la memoria que se asignará.

4. Luego, establecemos los datos del nuevo nodo  temp como un parámetro  x, que representa los datos recién agregados a la cola.

(LQ->rear)->next A continuación, establezca  el puntero del siguiente nodo del nodo temporal  en tempy agregue el nuevo nodo después del nodo final.

5. Finalmente, actualice el puntero de cola  LQ->rearpara que apunte al nuevo nodo de cola  temp.

6. De esta manera, se completa la operación de puesta en cola.

Ⅳ  Desencolar

[Definición] Fuera del equipo:  elimine el elemento principal en el equipo encadenado y envíelo  a

  • Considere subdesbordamiento no
  • no se desborda, entonces:
  1. Tome la temperatura del nodo principal de la cola;
  2. Envía el elemento principal a x;
  3. Eliminar el nodo principal del equipo encadenado;
  4. Si solo hay un elemento en la cola encadenada, la cola estará vacía después de la eliminación y el puntero de cola de la cola debe modificarse;
  5. La temperatura del nodo regresa al sistema.

【Diagrama esquemático】

【Código de ejemplo】

// 出队操作
int OutQueue(LkQue* LQ)
{ 
    LkQueNode* temp;  // 创建一个临时结点指针

    if (EmptyQueue(*LQ))  // 判断队列是否为空
    { 
        error("队空");  // 输出错误提示信息
        return 0;  // 返回0表示出队失败
    }
    else {
        temp = (LQ->front)->next;  // 获取队头结点的下一个结点
        (LQ->front)->next = temp->next;  // 将队头指针指向下一个结点

        if (temp->next == NULL)  // 如果出队后队列为空
            LQ->rear = LQ->front;  // 更新队尾指针为队头指针

        free(temp);  // 释放出队的结点
        return 1;  // 返回1表示出队成功
    }
}

【Explicación del código】

1. La función del código es realizar la operación de eliminación de la cola de la cadena.

2. La siguiente es una explicación de comentario para cada línea de código:

  • LkQue* LQ: el parámetro de puntero de cola encadenado entrante, que se utiliza para operar colas relacionadas.

3. En la función, primero juzgamos si la cola está vacía y  EmptyQueue(*LQ) juzgamos llamando a la función. Si está vacío, se generará un mensaje de error  "队空"y se devolverá 0 para indicar un error en la eliminación de la cola.

4. Si la cola no está vacía, ejecute la operación de eliminación de cola.

5. Primero apuntamos el puntero de nodo temporal  temp al siguiente nodo del nodo principal de la cola, es decir, el nodo que se va a quitar de la cola.

6.(LQ->front)->next Luego,  configure  el puntero del siguiente nodo del nodo principal de la cola  para que sea temp->next, es decir, apunte el puntero del comienzo de la cola al siguiente nodo del nodo de salida de la cola.

7. A continuación, verificamos si la cola está vacía después de la operación de eliminación de cola y determinamos  temp->next si lo está  NULL . En caso afirmativo, significa que la cola está vacía después de salir de la cola, y el puntero de cola de cola debe actualizarse  LQ->rear al puntero de cabeza de cola  LQ->front.

8. Finalmente, use  free la función para liberar el espacio de los nodos que se descartan de la cola para evitar pérdidas de memoria.

9. La función devuelve 1 para indicar que la eliminación de la cola se realizó correctamente. De esta forma, se completa la operación de dequeue.

Ⅴ  Obtener el primer elemento de la cola

【Código de ejemplo】

// 获取队头元素的值
DataType GetHead(LkQue LQ)
{ 
    LkQueNode* temp;  // 创建一个临时结点指针

    if (EmptyQueue(LQ))  // 判断队列是否为空
        return NULLData;  // 如果队列为空,则返回空数据

    else 
    {
        temp = LQ.front->next;  // 获取队头结点的下一个结点
        return temp->data;  // 返回队头结点的数据
    }
}

【Explicación del código】

1. La función del código es obtener el valor del elemento cabeza de la cola encadenada.

2. La siguiente es una explicación de comentario para cada línea de código:

  • DataType GetHead(LkQue LQ): Obtiene el valor del elemento al principio de la cola, y el tipo de valor de retorno es  DataType.
  • LkQue LQ: el parámetro de la cola encadenada entrante, que se utiliza para obtener el elemento principal de la cola.

3. En la función, primero juzgamos si la cola está vacía y  EmptyQueue(LQ) juzgamos llamando a la función. Si la cola está vacía, devuelve datos vacíos directamente  NULLData.

4. Si la cola no está vacía, realizar la operación de obtención del elemento de cabeza de cola.

5. Primero apuntamos el puntero de nodo temporal  temp al siguiente nodo del nodo de cabeza de cola, es decir, el elemento de cabeza de cola que se va a obtener.

6. Luego, devuelva los datos del nodo principal de la cola  temp->data.

7. De esta forma se completa la operación de obtención del valor del elemento cabeza.



3. matriz

Matriz: puede considerarse como una tabla lineal especial. La característica especial es que los elementos también son una tabla lineal. 

(1) Estructura lógica y operaciones básicas de arreglos

【Definición】Matriz  es una extensión de una tabla lineal , cada elemento del cual se compone de un valor y un conjunto de subíndices, y el número de subíndices se denomina dimensión de la matriz .

  • Las matrices son el tipo de datos más familiar y, en los primeros lenguajes de alto nivel, las matrices eran el único tipo de datos disponible.
  • Dado que cada elemento de la matriz tiene un tipo uniforme y los subíndices de los elementos de la matriz generalmente tienen límites superior e inferior fijos, el procesamiento de la matriz es más simple que otras estructuras complejas.
  • Las matrices multidimensionales son una generalización de las listas lineales.

[Nota] Una vez que se define una matriz, su dimensión y el límite de la dimensión no cambiarán. Por lo tanto , además de la inicialización y destrucción de estructuras, los arreglos generalmente tienen solo dos operaciones básicas :
  1. read  Dado un conjunto de subíndices, lee el elemento de datos correspondiente
  2. Escribir  dado un conjunto de subíndices, modifique los elementos de datos correspondientes

[Matriz bidimensional] Matriz bidimensional: La matriz bidimensional  A mn  puede considerarse como  un vector compuesto por m vectores fila , o como  un vector compuesto por n vectores columna .

【Diagrama esquemático】 


(2) La estructura de almacenamiento de la matriz 

①Estructura de almacenamiento  - estructura de almacenamiento secuencial

  • Dado que la estructura de memoria de la computadora es unidimensional , para representar una matriz multidimensional con memoria unidimensional, es necesario organizar los elementos de la matriz en una secuencia en un cierto orden y luego almacenar esta secuencia lineal en la memoria.
  • Y debido a que la matriz generalmente no se inserta ni elimina, es decir, una vez que se establece la matriz, la cantidad de elementos en la estructura y la relación entre los elementos no cambiará. Por lo tanto, el método de almacenamiento secuencial se usa generalmente para representar la matriz.

②  Fórmula de direccionamiento (principalmente almacenada por fila)

La matriz bidimensional Amn  se almacena en la memoria en "orden de fila principal", suponiendo que cada elemento ocupa k unidades de almacenamiento :
  • La dirección de almacenamiento del elemento aij debe  ser la dirección base de la matriz más el número de celdas ocupadas por los elementos delante de aij  .
  • Debido a que a ij  está ubicado en la i-ésima fila y la j-ésima columna, hay i × n elementos en la i fila frontal, y hay j elementos frente a a ij  en la i-ésima fila, por lo que hay i ×n+j elementos delante de él, entonces, la función de cálculo de dirección de un ij  es:
    LOC(aij)=LOC(a00)+(i*n+j)*k

(3) almacenamiento comprimido de matriz

Para ahorrar espacio de almacenamiento , podemos comprimir y almacenar este tipo de matriz : es decir, solo se asigna un espacio de almacenamiento para múltiples elementos idénticos distintos de cero; no se asigna espacio para elementos cero .

① Matriz especial

Matriz especial  ,  es decir, una matriz en la que la distribución de elementos distintos de cero o elementos cero tiene ciertas reglas .
Almacenamiento comprimido de varias matrices especiales:
  • Matriz simétrica
  • matriz triangular 
  • matriz dispersa

② Matriz especial: matriz simétrica 

Ⅰ Definición

[Definición] En una matriz cuadrada A de orden n, si los elementos satisfacen las siguientes propiedades, entonces A se denomina matriz simétrica:
  • aij = ají
  • 0≤i
  • j≤n-1

[Ejemplo] La siguiente figura es una matriz simétrica de orden 5:

Ⅱ Almacenamiento físico

  • Los elementos de la matriz simétrica son simétricos con respecto a la diagonal principal, por lo que siempre que se almacenen los elementos del triángulo superior o del triángulo inferior de la matriz, deje que cada dos elementos simétricos compartan un espacio de almacenamiento , de modo que casi la mitad del espacio de almacenamiento puede ser salvado.
  • Sin pérdida de generalidad, almacenamos elementos debajo de la diagonal principal (incluidas las diagonales) en "primer orden de fila", y el formato de almacenamiento se muestra en la figura:

  • En esta matriz triangular inferior, la fila i tiene exactamente i elementos y el número total de elementos es: 
    ∑(i)=n(n+1)/2
  • Por tanto, podemos almacenar estos elementos en un arreglo unidimensional  S[0..n(n+1)/2-1] en el orden indicado por las flechas en la figura  .
  • Para facilitar el acceso a los elementos de la matriz simétrica A, debemos encontrar una correspondencia entre aij y S[k].

 

Fórmula de transformación de subíndice: (representado por el triángulo a continuación )

  • Si  i≥j , entonces aij está en el triángulo inferior: hay 1+2+…+i=i(i+1)/2 elementos en la fila i antes de aij (desde la fila 0 hasta la fila i-1), En línea i, hay exactamente j elementos antes de aij (es decir, ai0, ai1, ai2, ai3,…,ai j-1), entonces:
  • Si  i<j , entonces aij está en la matriz triangular superior: debido a que aij=aji, simplemente intercambie i y j en la relación correspondiente anterior para obtener:

  • Con la relación de intercambio de subíndices anterior, para cualquier conjunto dado de subíndices (i, j), el elemento de matriz aij se puede encontrar en S[k], de lo contrario, para todo k=0,1,2,3,...n (n+1)/2-1, puede determinar la posición (i, j) del elemento en S[k] en la matriz. Por lo tanto, S[n(n+1)/2] se denomina almacenamiento comprimido de la matriz simétrica A, como se muestra en la siguiente figura:
  • Por ejemplo, a31 y a13 se almacenan en sa[7] porque: 
    k=i*(i+1)/2+j=3*(3+1)/2+1=7

③ Matriz especial: matriz triangular

Ⅰ Definición 

[Definición] Dividida por la diagonal principal, la matriz triangular tiene dos tipos: triangular superior y triangular inferior:
  • La matriz triangular superior se muestra en la figura, y los elementos en su triángulo inferior (excluyendo la diagonal principal) son todos constantes.
  • Una matriz triangular inferior es todo lo contrario, con constantes por encima de la diagonal principal, como se muestra en la figura.
  • En la mayoría de los casos, la constante de la matriz triangular es cero.

【Ejemplo】

 

【ilustrar】

El elemento repetido c en la matriz triangular puede compartir un espacio de almacenamiento, y los elementos restantes tienen exactamente  n(n+1) / 2.  Por lo tanto, la matriz triangular se puede comprimir y almacenar en el vector s[0..n(n +1)/2 ], donde c se almacena en el último componente del vector. 

Ⅱ Matriz triangular superior

[Explicación] En la matriz triangular superior , la p-ésima fila (0≤p<n) sobre la diagonal principal tiene exactamente np elementos. Cuando los elementos a ij  en la matriz triangular superior se almacenan en el orden de la fila , el  There son (np)=i(2n-i+1)/2 elementos en la línea i, y hay exactamente ji elementos antes de a ij en la línea i: ij , a ij +1 , a ij-1 .

[Fórmula] Por lo tanto, la relación correspondiente entre s[k] y aij es  :

Ⅲ Matriz triangular inferior

[Fórmula] El almacenamiento de la matriz triangular inferior es similar al de una matriz simétrica, y la relación correspondiente entre s[k] y aij es:

 

④ Matriz especial: matriz dispersa 

Ⅰ Definición

Matriz dispersa : suponga que hay s elementos distintos de cero en la matriz A, si s es mucho más pequeño que el número total de elementos de la matriz, entonces A se denomina matriz dispersa .

  • Almacenamiento comprimido de matrices dispersas  es decir, solo se almacenan elementos distintos de cero en matrices dispersas.
  • Propósito: Ahorra espacio de almacenamiento .

Ⅱ trillizos

  • Dado que la distribución de elementos distintos de cero es generalmente irregular , la posición (i, j) de la fila y la columna donde se encuentra también debe registrarse mientras se almacena el elemento distinto de cero.
  • Por el contrario, un triplete (i, j, a ij ) identifica de forma única una entrada distinta de cero de la matriz A.
  • Por lo tanto, una matriz dispersa se puede identificar de forma única mediante tripletas que representan elementos distintos de cero y su número de filas y columnas.

 

Ⅲ Mesa triple

La siguiente tabla de tripletes:

( (0,1,12),(0,2,9),(2,0,- 3),(2,5,14),(3,2,24), (4,1,18), (5,0,15),(5,3,-7) )
  • Agregar el par de valores de fila y columna (5,6) se puede usar como otra descripción de la siguiente matriz M.
  • Los diferentes métodos de representación de la tabla triple mencionada anteriormente pueden conducir a diferentes métodos de almacenamiento de compresión para matrices dispersas.

   

Ⅳ Lista de secuencias triples

[Representación] La representación de la tabla de secuencia triple de la matriz dispersa——  Convierta los elementos distintos de cero en la matriz en triples y guárdelos en la memoria en el orden no decreciente de las filas (orden creciente de las filas en las columnas) .
[Estructura de tabla triple] Estructura de tabla triple: suponiendo que la tabla triple está representada por una estructura de almacenamiento secuencial, se puede obtener el momento escaso
Un método de almacenamiento comprimido para arreglos: tabla de secuencia triple.
[Código de ejemplo] Tipo de almacenamiento de tabla de triplete de matriz dispersa:
const int maxnum = 10;  // 定义非零元素的最大个数为10

typedef struct node
{
    int i, j;  // 非零元的行下标和列下标
    DataType v;  // 非零元素的值
} NODE;  // 定义三元组结构体

// 定义稀疏矩阵结构体
typedef struct spmatrix
{
    NODE data[maxnum];  // 非零元三元组表
    int mu, nu, tu;  // 矩阵的行数、列数和非零元个数
} SpMtx;

[Diagrama esquemático] Supongamos: SpMtrx M ; Entonces la representación del triplete de la matriz dispersa que se muestra en la siguiente figura es la siguiente:

【Explicación del código】

1. La siguiente es una explicación de comentario para cada línea de código:

  • const int maxnum = 10; Se define una constante  maxnumque indica que el número máximo de elementos distintos de cero es 10.
  • typedef struct node define un  node tipo de estructura llamado .
  • int i, j; node En la estructura se definen  dos variables enteras i y  jque representan respectivamente el subíndice de fila y el subíndice de columna de elementos distintos de cero.
  • DataType v;  Una variable de tipo  node se define en la estructura  que representa el valor del elemento distinto de cero.DataTypev
  • NODE; Se define  el tipo NODE , que es una estructura de triplete.
  • typedef struct spmatrix define un  spmatrix tipo de estructura llamado .
  • NODE data[maxnum]; Se define una matriz en  spmatrix la estructura  data, que se utiliza para almacenar la tabla triple de elementos distintos de cero. El tamaño de la matriz es  maxnum, que es el número máximo de elementos distintos de cero.
  • int mu, nu, tu; Tres variables enteras ,  y  spmatrix se definen en la estructura  representando respectivamente el número de filas, columnas y elementos distintos de cero de la matriz.munutu
  • SpMtx; El tipo está definido  SpMtx , que es una estructura de matriz dispersa.

2. Este código define una estructura de datos de matriz dispersa  SpMtx, que consta de una matriz de tripletes  data y tres variables enteras  mu, nu y  tu .

3. La  data matriz se utiliza para almacenar la tabla de tripletes de los elementos distintos de cero de la matriz dispersa, y  mu, nu y  tu representa el número de filas, columnas y elementos distintos de cero de la matriz, respectivamente. 

4.maxnum  Limite el número máximo de elementos distintos de cero.

Supongo que te gusta

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