Inversión por segmentos de lista enlazada simple

6-9 Inversión por segmentos de lista enlazada simple (25 puntos)

Dado una lista enlazada simple con nodo cabecera y un antero K, debes invertir cada grupo de K nodos en la lista. Por ejemplo, dada la lista 1→2→3→4→5→6 y K=3, necesitas transformarla a 3→2→1→6→5→4; si K=4, deberías obtener 4→3→2→1→5→6.

Definición de la interfaz:

void segmentInvert(List L, int K);

Donde la estructura List se define como:

typedef struct Node *PointerNode;
struct Node {
    DataType Data; /* Almacena los datos del nodo */
    PointerNode Next; /* Puntero al siguiente nodo */
};
typedef PointerNode List; /* Define el tipo de lista enlazada simple */

L es la lista enlazada simple con nodo cabecrea, K es la longitud de cada segmanto. La función segmentInvert debe invertir los nodos en L según los requisitos por segmentos.

Ejemplo de programa de prueba:

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

typedef int DataType;

typedef struct Node *PointerNode;
struct Node {
    DataType Data; /* Almacena los datos del nodo */
    PointerNode Next; /* Puntero al siguiente nodo */
};
typedef PointerNode List; /* Define el tipo de lista enlazada simple */

List readInput(); /* Implementado por el juez, detalles omitidos */
void printList(List L); /* Implementado por el juez, detalles omitidos */
void segmentInvert(List L, int K);

int main()
{
    List L;
    int K;

    L = readInput();
    scanf("%d", &K);
    segmentInvert(L, K);
    printList(L);

    return 0;
}

/* Tu código será insertado aquí */

Caso de entrada:

6
1 2 3 4 5 6
4

Caso de salida:

4 3 2 1 5 6

void segmentInvert(List L, int K){
    PointerNode cursor, nextNode, connector1, connector2, head = L->Next;
    
    /* connector1 y connector2 se utilizan para conectar segmentos invertidos;
       inicialmente establecemos connector1 apuntando al nodo cabecera, 
       connector2 apuntando al nodo con valor 1, ¿por qué?
       porque los primeros cuatro elementos deben insertarse mediante inserción
       por cabeza detrás de L, es decir, detrás de connector1;
       cuando llegamos al nodo 5, debemos hacer connector1 = connector2; connector2 = nodo 5;
       luego debemos insertar 5 6 7 8 mediante inserción por cabeza detrás del nodo 1,
       es decir, detrás de connector1
       significa que siempre insertamos nodos mediante inserción por cabeza detrás de connector1,
       mientras connector2 registra el primer nodo del siguiente segmento,
       es decir, el próximo connector1, actualizando continuamente connector1
       podemos lograr la inversión por segmentos de toda la lista;
       esta es la función de connector1 y connector2
    */
    connector1 = L;
    connector2 = L->Next;
    cursor = L->Next;
    nextNode = cursor->Next;
    int totalNodes = 0;
    
    /*
        totalNodes registra el número total de nodos;
        ¿Por qué registrar el total?
        porque necesitamos saber si el último segmento tiene menos de K nodos o exactamente K;
        si tiene menos, no se invierte, si es exactamente K, también se invierte ese segmento;
    */
    if (K >= 1) {
        // contar total
        while (head) {
            totalNodes++;
            head = head->Next;
        }
        
        // verificar, si total < K no cambiar la lista
        if (totalNodes >= K) {
            /* totalNodes/K es cuántos segmentos necesitamos recorrer, como el resultado de totalNodes/K es cociente,
               entonces si totalNodes no es divisible por K, es decir, el último segmento tiene menos de K, no lo recorremos
               es decir, no lo tocamos.
            */
            for (int segment = 0; segment < totalNodes / K; segment++) {
                // cada segmento recorre K veces
                for(int pos = 0; pos < K; pos++) {
                    cursor->Next = connector1->Next; // inserción por cabeza
                    connector1->Next = cursor;
                    cursor = nextNode;
                    /*
                     ¿Por qué verificar si nextNode no es nulo?
                     porque inicialmente establecí: cursor = L->Next;
                                     nextNode = cursor->Next;
                     así que finalmente nextNode apunta al campo Next del último nodo de la lista, que es nulo,
                     por lo tanto no existe nextNode->Next, si no verificamos, el programa no seguirá ejecutándose
                     pero durante la compilación inicial no dará error, por lo tanto este punto es muy importante,
                     es decir, verificar problemas de límites, encontré este punto después de buscar por mucho tiempo.
                    */
                    if(nextNode)
                        nextNode = nextNode->Next;
                }
                connector1 = connector2; // actualizar connector1 y connector2
                connector2 = cursor;
            }
            connector1->Next = cursor; // finalmente establecer el campo Next del nodo final como nulo.
        }
    }
}

Etiquetas: estructuras de datos Listas Enlazadas algoritmos de inversión manipulación de punteros

Publicado el 9-23 17:07