44 - Estructuras dinámicas en C: Listas genéricas


Continuando con el tema de listas trabajaremos con las listas genéricas en C. Una lista se comporta como genérica cuando las inserciones y extracciones se realizan en cualquier parte de la lista.
Codificaremos una serie de funciones para administrar listas genéricas.

Funciones a desarrollar:

Inserta un nodo en la posición (pos) y con la información que hay en el parámetro x.

    void insertar(int pos, int x)

Extrae la información del nodo de la posición indicada (pos). Se debe eliminar el nodo.

    int extraer(int pos)

Borra el nodo de la posición (pos).

    void borrar(int pos)

Intercambia las informaciones de los nodos de las posiciones pos1 y pos2.

   
    void intercambiar(int pos1,int pos2)

Retorna el valor del nodo con mayor información.

    int mayor()

Retorna la posición del nodo con mayor información.

    int posMayor()

Retorna la cantidad de nodos de la lista.

    int cantidad()

Debe retornar 1 si la lista está ordenada de menor a mayor, 0 en caso contrario.

    int ordenada()

Debe retornar 1 si existe la información que llega en el parámetro, 0 en caso contrario.

    int existe(int info)

La función vacía debe retornar 1 si está vacía y 0 si no lo está.

    int vacia()

Programa: programa166.c

Ver video

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


struct nodo {
    int info;
    struct nodo *sig;
};

struct nodo *raiz=NULL;


void liberar()
{
    struct nodo *reco = raiz;
    struct nodo *bor;
    while (reco != NULL)
    {
        bor = reco;
        reco = reco->sig;
        free(bor);
    }
}

int vacia()
{
    if (raiz == NULL)
        return 1;
    else
        return 0;
}

int cantidad()
{
    struct nodo *reco = raiz;
    int cant = 0;
    while (reco != NULL)
    {
        cant++;
        reco = reco->sig;
    }
    return cant;
}

void insertar(int pos, int x)
{
    if (pos <= cantidad() + 1)
    {
        struct nodo *nuevo;
        nuevo=malloc(sizeof(struct nodo));
        nuevo->info = x;
        if (pos == 1)
        {
            nuevo->sig = raiz;
            raiz = nuevo;
        }
        else
            if (pos == cantidad() + 1)
            {
                struct nodo *reco = raiz;
                while (reco->sig != NULL)
                {
                    reco = reco->sig;
                }
                reco->sig = nuevo;
                nuevo->sig = NULL;
            }
            else
            {
                struct nodo *reco = raiz;
                int f;
                for (f = 1; f <= pos - 2; f++)
                {
                    reco = reco->sig;
                }
                struct nodo *siguiente = reco->sig;
                reco->sig = nuevo;
                nuevo->sig = siguiente;
            }
    }
}

void imprimir()
{
    struct nodo *reco=raiz;
    printf("Lista completa.\n");
    while (reco!=NULL)
    {
        printf("%i ",reco->info);
        reco=reco->sig;
    }
    printf("\n");
}


int extraer(int pos)
{
    if (pos <= cantidad())
    {
        int informacion;
        struct nodo *bor;
        if (pos == 1)
        {
            informacion = raiz->info;
            bor = raiz;
            raiz = raiz->sig;
        }
        else
        {
            struct nodo *reco;
            reco = raiz;
            int f;
            for (f = 1; f <= pos - 2; f++)
            {
                reco = reco->sig;
            }
            struct nodo *prox = reco->sig;
            reco->sig = prox->sig;
            informacion = prox->info;
            bor = prox;
        }
        free(bor);
        return informacion;
    }
    else
        return -1;
}


void borrar(int pos)
{
    if (pos <= cantidad())
    {
        struct nodo *bor;
        if (pos == 1)
        {
            bor = raiz;
            raiz = raiz->sig;
        }
        else {
            struct nodo *reco;
            reco = raiz;
            int f;
            for (f = 1; f <= pos - 2; f++)
            {
                reco = reco->sig;
            }
            struct nodo *prox = reco->sig;
            reco->sig = prox->sig;
            bor = prox;
        }
        free(bor);
    }
}


void intercambiar(int pos1, int pos2)
{
    if (pos1 <= cantidad() && pos2 <= cantidad())
    {
        struct nodo *reco1 = raiz;
        int f;
        for (f = 1; f < pos1; f++)
        {
            reco1 = reco1->sig;
        }
        struct nodo *reco2 = raiz;
        for (f = 1; f < pos2; f++)
        {
            reco2 = reco2->sig;
        }
        int aux = reco1->info;
        reco1->info = reco2->info;
        reco2->info = aux;
    }
}


int mayor()
{
    if (!vacia())
    {
        int may = raiz->info;
        struct nodo *reco = raiz->sig;
        while (reco != NULL)
        {
            if (reco->info > may)
            {
                may = reco->info;
            }
            reco = reco->sig;
        }
        return may;
    }
    else
        return -1;
}


int posMayor()
{
    if (!vacia())
    {
        int may = raiz->info;
        int x = 1;
        int pos = x;
        struct nodo *reco = raiz->sig;
        while (reco != NULL)
        {
            x++;
            if (reco->info > may)
            {
                may = reco->info;
                pos = x;
            }
            reco = reco->sig;
        }
        return pos;
    }
    else
        return -1;
}

int ordenada()
{
    if (cantidad()>1)
    {
        struct nodo *reco1 = raiz;
        struct nodo *reco2 = raiz->sig;
        while (reco2 != NULL)
        {
            if (reco2->info<reco1->info)
            {
                return 0;
            }
            reco2 = reco2->sig;
            reco1 = reco1->sig;
        }
    }
    return 1;
}

int existe(int x)
{
    struct nodo *reco = raiz;
    while (reco != NULL)
    {
        if (reco->info == x)
            return 1;
        reco = reco->sig;
    }
    return 0;
}


int main()
{
    insertar(1, 10);
    insertar(2, 20);
    insertar(3, 30);
    insertar(2, 15);
    insertar(1, 115);
    imprimir();
    printf("Luego de Borrar el primero\n");
    borrar(1);
    imprimir();
    printf("Luego de Extraer el segundo\n");
    extraer(2);
    imprimir();
    printf("Luego de Intercambiar el primero con el tercero\n");
    intercambiar(1, 3);
    imprimir();
    if (existe(20))
        printf("Se encuentra el 20 en la lista\n");
    else
        printf("No se encuentra el 20 en la lista\n");
    printf("La posicion del mayor es:%i\n", posMayor());
    if (ordenada())
        printf("La lista esta ordenada de menor a mayor\n");
    else
        printf("La lista no esta ordenada de menor a mayor\n");
    liberar();
    getch();
    return 0;
}

Para insertar en una determinada posición dentro de la lista:

    void insertar (int pos, int x)

Primero con un if verificamos que exista esa posición en la lista (por ejemplo si la lista tiene 4 nodos podemos insertar hasta la posición 5, es decir uno más allá del último):

    if (pos <= cantidad() + 1)

Si ingresa al if ya podemos crear el nodo:

        struct nodo *nuevo;
        nuevo=malloc(sizeof(struct nodo));

Ahora debemos analizar si la inserción es al principio de la lista, al final o en medio ya que los enlaces varían según donde se lo inserta.

Para saber si se inserta al principio de la lista preguntamos si en pos llega un 1:

        if (pos == 1)

Si llega un 1 luego enlazamos el puntero sig del nodo que creamos con la dirección del primer nodo de la lista (raiz apunta siempre al primer nodo de la lista) y luego desplazamos raiz al nodo que acabamos de crear:

            nuevo->sig = raiz;
            raiz = nuevo;

Si no se inserta al principio de la lista preguntamos si se inserta al final:

            if (pos == cantidad() + 1)    

En caso de insertarse al final recorremos la lista hasta el último nodo:

                struct nodo *reco = raiz;
                while (reco->sig != NULL)
                {
                    reco = reco->sig;
                }

y enlazamos el puntero sig del último nodo de la lista con la dirección del nodo que acabamos de crear (disponemos en sig del nodo creado el valor NULL ya que no hay otro nodo más adelante)

                reco->sig = nuevo;
                nuevo->sig = NULL;

Si no se inserta al principio o al final significa que tenemos que insertar en medio de la lista.
Disponemos un for donde avanzamos un puntero auxiliar y nos detenemos una posición antes a donde tenemos que insertarlo:

                struct nodo *reco = raiz;
                int f;
                for (f = 1; f <= pos - 2; f++)
                {
                    reco = reco->sig;
                }

Disponemos otro puntero auxiliar que apunte al nodo próximo a donde está apuntando reco. Ahora enlazamos el puntero sig del nodo apuntado por reco con la dirección del nodo creado y el puntero sig del nodo creado con la dirección del nodo siguiente:

                struct nodo *siguiente = reco->sig;
                reco->sig = nuevo;
                nuevo->sig = siguiente;


La función extraer recibe como parámetro la posición del nodo a extraer:

    int extraer (int pos) 

Primero verificamos que la posición exista en la lista:

    if (pos <= cantidad())    

En caso que exista verificamos si el nodo a extraer es el primero de la lista (este análisis debe hacerse ya que si es el primero de la lista se modifica el puntero raiz):

        if (pos == 1) 

Si es el primero guardamos en una variable auxiliar la información del nodo, inicializamos bor con la dirección del nodo a borrar y avanzamos el puntero raiz:

            informacion = raiz->info;
            bor = raiz;
            raiz = raiz->sig;

Si el nodo a extraer no está al principio de la lista avanzamos con una estructura repetitiva hasta el nodo anterior a extraer:

            struct nodo *reco;
            reco = raiz;
            int f;
            for (f = 1; f <= pos - 2; f++)
            {
                reco = reco->sig;
            }

Luego definimos otro puntero auxiliar y lo disponemos en el siguiente nodo a donde está apuntando reco:

            struct nodo *prox = reco->sig;

Ahora enlazamos el puntero sig del nodo apuntado por reco al nodo siguiente del nodo apuntado por prox (es decir el nodo apuntado por prox queda fuera de la lista):

            reco->sig = prox->sig;

Inicializamos la variable auxiliar informacion con el dato a devolver y bor con la dirección del nodo a borrar.

            informacion = prox->info;
            bor = prox;

Por último borramos el nodo a extraer:

        free(bor);


La funcion 'borrar', elimina el nodo de una determinada posición es muy similar a extraer con la diferencia de que no retorna valor:

void borrar(int pos)
{
    if (pos <= cantidad())
    {
        struct nodo *bor;
        if (pos == 1)
        {
            bor = raiz;
            raiz = raiz->sig;
        }
        else {
            struct nodo *reco;
            reco = raiz;
            int f;
            for (f = 1; f <= pos - 2; f++)
            {
                reco = reco->sig;
            }
            struct nodo *prox = reco->sig;
            reco->sig = prox->sig;
            bor = prox;
        }
        free(bor);
    }
}


La función intercambiar recibe dos enteros que representan las posiciones de los nodos que queremos intercambiar sus informaciones:

    void intercambiar (int pos1, int pos2) 

Mediante un if verificamos que las dos posiciones existan en la lista:

    if (pos1 <= cantidad() && pos2 <= cantidad())    

Definimos un puntero auxiliar llamado reco1, lo inicializamos con la dirección del primer nodo y mediante un for avanzamos hasta la posición almacenada en pos1:

        struct nodo *reco1 = raiz;
        int f;
        for (f = 1; f < pos1; f++)
        {
            reco1 = reco1->sig;
        }

De forma similar con un segundo puntero auxiliar avanzamos hasta la posición indicada por pos2:

        struct nodo *reco2 = raiz;
        for (f = 1; f < pos2; f++)
        {
            reco2 = reco2->sig;
        }

Por último intercambiamos las informaciones que almacenan cada nodo:

        int aux = reco1->info;
        reco1->info = reco2->info;
        reco2->info = aux;

La función que retorna el mayor de la lista:

    int mayor () 

Verificamos que la lista no esté vacía:

   if (!vacia()) 

Suponemos que el mayor es el primero de la lista e inicializamos un puntero auxiliar con la dirección del segundo nodo de la lista:

        int may = raiz->info;
        struct nodo *reco = raiz->sig;

Mediante una estructura repetitiva recorremos toda la lista:

        while (reco != NULL) 

Cada vez que encontramos un nodo con información mayor que la variable may la actualizamos con este nuevo valor y avanzamos el puntero reco para visitar el siguiente nodo:

            if (reco->info > may)
            {
                may = reco->info;
            }
            reco = reco->sig;

Fuera de la estructura repetitiva retornamos el mayor:

        return may;

La función que retorna la posición del mayor es similar al anterior con la salvedad que debemos almacenar en otro auxiliar la posición donde se almacena el mayor:

int posMayor() 
{
    if (!vacia())
    {
        int may = raiz->info;
        int x = 1;
        int pos = x;
        struct nodo *reco = raiz->sig;
        while (reco != NULL)
        {
            if (reco->info > may)
            {
                may = reco->info;
                pos = x;
            }
            reco = reco->sig;
            x++;
        }
        return pos;
    }
    else
        return -1;
}

La función que debe retornar si está ordenada la lista de menor a mayor es:

    int ordenada() 

Lo primero que verificamos si la lista tiene más de un nodo significa que debemos controlarla:

    if (cantidad()>1) 

Disponemos dos punteros auxiliares con las direcciones del primer y segundo nodo de la lista:

        struct nodo *reco1 = raiz;
        struct nodo *reco2 = raiz->sig;

Mediante un while mientras no se finaliza la lista:

        while (reco2 != NULL) 

controlamos si la información del segundo nodo es menor al nodo anterior significa que la lista no está ordenada y podemos parar el análisis retornando un 0

            if (reco2->info<reco1->info)
            {
                return 0;
            }

Dentro del while avanzamos los dos punteros a sus nodos siguientes respectivamente.

            reco2 = reco2->sig;
            reco1 = reco1->sig;

Fuera del while retornamos un 1 indicando que la lista está ordenada de menor a mayor

    return 1;


El función existe:


    int existe(int x) 

Mediante un while recorremos la la lista:

    struct nodo *reco = raiz;
    while (reco != NULL)

y en cada nodo que visitamos controlamos si el parámetro x es igual a la información del nodo, en caso afirmativo salimos de la función retornando un 1:

        if (reco->info == x)
            return 1;
        reco = reco->sig;

Fuera del while retornamos un 0 indicando que ningún nodo coincide con el parámetro x:

    return 0;


Problemas propuestos

  1. Plantear un programa para administrar una lista genérica implementando las siguientes funciones:
    a) Insertar un nodo al principio de la lista.
    b) Insertar un nodo al final de la lista.
    c) Insertar un nodo en la segunda posición. Si la lista está vacía no se inserta el nodo.
    d) Insertar un nodo en la ante última posición.
    e) Borrar el primer nodo.
    f) Borrar el segundo nodo.
    g) Borrar el último nodo.
    h) Borrar el nodo con información mayor.

    Ver video

Solución
programa167.c

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


struct nodo {
    int info;
    struct nodo *sig;
};

struct nodo *raiz=NULL;


void liberar()
{
    struct nodo *reco = raiz;
    struct nodo *bor;
    while (reco != NULL)
    {
        bor = reco;
        reco = reco->sig;
        free(bor);
    }
}

int vacia()
{
    if (raiz == NULL)
        return 1;
    else
        return 0;
}

int cantidad()
{
    struct nodo *reco = raiz;
    int cant = 0;
    while (reco != NULL)
    {
        cant++;
        reco = reco->sig;
    }
    return cant;
}

void imprimir()
{
    struct nodo *reco=raiz;
    printf("Lista completa.\n");
    while (reco!=NULL)
    {
        printf("%i ",reco->info);
        reco=reco->sig;
    }
    printf("\n");
}

void insertarPrimero(int x)
{
    struct nodo *nuevo;
    nuevo=malloc(sizeof(struct nodo));
    nuevo->info = x;
    nuevo->sig = raiz;
    raiz = nuevo;
}

void insertarUtlimo(int x)
{
    struct nodo *nuevo;
    nuevo=malloc(sizeof(struct nodo));
    nuevo->info = x;
    nuevo->sig=NULL;
    if (raiz == NULL)
        raiz = nuevo;
    else
    {
        struct nodo *reco = raiz;
        while (reco->sig != NULL)
        {
            reco = reco->sig;
        }
        reco->sig = nuevo;
    }
}

void insertarSegundo(int x)
{
    if (raiz != NULL)
    {
        struct nodo *nuevo;
        nuevo=malloc(sizeof(struct nodo));
        nuevo->info = x;
        if (raiz->sig == NULL)
        {
            raiz->sig = nuevo;
        }
        else
        {
            nuevo->sig = raiz->sig;
            raiz->sig = nuevo;
        }
    }
}


void insertarAnteUltimo(int x)
{
    if (raiz != NULL)
    {
        struct nodo *nuevo;
        nuevo=malloc(sizeof(struct nodo));
        nuevo->info = x;
        if (raiz->sig == NULL) {
            nuevo->sig = raiz;
            raiz = nuevo;
        }
        else
        {
            struct nodo *atras = raiz;
            struct nodo *reco = raiz->sig;
            while (reco->sig != NULL)
            {
                atras = reco;
                reco = reco->sig;
            }
            nuevo->sig = atras->sig;
            atras->sig = nuevo;
        }
    }
}

void borrarPrimero()
{
    if (raiz != NULL)
    {
        struct nodo *bor = raiz;
        raiz = raiz->sig;
        free(bor);
    }
}

void borrarSegundo()
{
    if (raiz != NULL)
    {
        if (raiz->sig != NULL)
        {
            struct nodo *bor = raiz->sig;
            struct nodo *tercero = raiz->sig;
            tercero = tercero->sig;
            raiz->sig = tercero;
            free(bor);
        }
    }
}

void borrarUltimo()
{
    if (raiz != NULL)
    {
        if (raiz->sig == NULL)
        {
            free(raiz);
            raiz = NULL;
        }
        else
        {
            struct nodo *reco = raiz->sig;
            struct nodo *atras = reco;
            while (reco->sig != NULL)
            {
                atras = reco;
                reco = reco->sig;
            }
            atras->sig = NULL;
            free(reco);
        }
    }
}


void borrarMayor()
{
    if (raiz != NULL)
    {
        struct nodo *reco = raiz;
        int may = raiz->info;
        while (reco != NULL)
        {
            if (reco->info>may)
            {
                may = reco->info;
            }
            reco = reco->sig;
        }
        reco = raiz;
        struct nodo *atras = raiz;
        struct nodo *bor;
        while (reco != NULL)
        {
            if (reco->info == may)
            {
                if (reco == raiz)
                {
                    bor = raiz;
                    raiz = raiz->sig;
                    atras = raiz;
                    reco = raiz;
                    free(bor);
                }
                else {
                    atras->sig = reco->sig;
                    bor = reco;
                    reco = reco->sig;
                    free(bor);
                }
            }
            else {
                atras = reco;
                reco = reco->sig;
            }
        }
    }
}

int main()
{
    insertarPrimero(10);
    insertarPrimero(45);
    insertarPrimero(23);
    insertarPrimero(89);
    imprimir();
    printf("Insertamos un nodo al final:\n");
    insertarUtlimo(160);
    imprimir();
    printf("Insertamos un nodo en la segunda posicion:\n");
    insertarSegundo(13);
    imprimir();
    printf("Insertamos un nodo en la anteultima posicion:\n");
    insertarAnteUltimo(600);
    imprimir();
    printf("Borramos el primer nodo de la lista:\n");
    borrarPrimero();
    imprimir();
    printf("Borramos el segundo nodo de la lista:\n");
    borrarSegundo();
    imprimir();
    printf("Borramos el ultimo nodo de la lista:\n");
    borrarUltimo();
    imprimir();
    printf("Borramos el mayor de la lista:\n");
    borrarMayor();
    imprimir();
    liberar();
    getch();
    return 0;
}

Retornar