Lista Simple

Estructura de archivos:

📁 09_Lista
└── 📁 Lista-NN
    ├── 📄 main.c
    └── 📄 Makefile

Ejercicios:

Lista Simple 01

Makefile


CC = gcc
CFLAGS = -std=c11 -Wall -Wextra -pedantic

PROG = main
OBJ = main.o

$(PROG): $(OBJ)
	$(CC) $(OBJ) -o $(PROG)

$(OBJ): main.c
	$(CC) $(CFLAGS) -c main.c

.PHONY: clean
clean:
	del $(OBJ) $(PROG).exe

Código:


// gcc -std=c11 -Wall -Wextra -pedantic main.c -o main

#include <stdio.h>
#include <stdlib.h> // Se usa system(), exit(), malloc(), free()
#include <conio.h> // Se usa getch() y getche()

/* Nodo de lista simplemente enlazada.
 * - Dato => valor almacenado.
 * - Sig  => puntero al siguiente nodo.
 */
struct Lis
{
    int Dato;
    struct Lis *Sig;
};

/* Prototipos de funciones */
void Ingresa(struct Lis **);
void Lee(struct Lis **);
void Borra(struct Lis **);
void Elimina(struct Lis **);

int main(void)
{
    int Opcion;

    /* Inicio apunta al primer nodo de la lista.
     * Si es NULL => lista vacia.
     */
    struct Lis *Inicio = NULL;

    /* Bucle infinito tipo menu */
    for (;;)
    {
        system("cls"); // Limpia la pantalla.

        printf("\n1 - Ingresa datos\n");
        printf("2 - Lee datos\n");
        printf("3 - Borra datos\n");
        printf("4 - Salir\n\n");
        printf("Ingrese una opcion (1 - 4): ");
        
        Opcion = getche(); // Lee el caracter y lo muestra, sin esperar Enter

        switch(Opcion)
        {
            case '1':
                Ingresa(&Inicio);
                break;

            case '2':
                Lee(&Inicio);
                break;

            case '3':
                Borra(&Inicio);
                break;

            case '4':
                /* Antes de salir, libera toda la memoria. */
                Elimina(&Inicio);
                exit(0);
        }
    }

    return 0;
}

/* Inserta nodo al final de la lista. 
 * **Principio es un puntero a puntero porque:
 * Necesitamos poder modificar el puntero original cuando la lista esta vacia.
 */
void Ingresa(struct Lis **Principio)
{
    /* *Actual:
     * - Se usa para recorrer la lista, comienza apuntando al primer nodo.
     *
     * *Nuevo:
     * - Sera el nodo recien creado con malloc().
     */
    struct Lis *Actual = *Principio, *Nuevo;

    system("cls"); // Limpia la pantalla.

    /* Se reserva memoria dinamica para un nodo.
     * sizeof() reserva spacio para Dato y *Sig
     * Si malloc devuelve NULL => no hay memoria disponible.
     */
    if (! (Nuevo = (struct Lis *)malloc(sizeof(struct Lis))))
    {
        system("cls"); // Limpia la pantalla.

        printf("\n\nNo hay memoria disponible\n");
        
        printf("\n\nPresiones una tecla para continuar\n");
        getch(); // Espera que se presione una tecla.

        return;
    }

    printf("\n\nIngrese el dato: ");
    /* Se carga el valor en el nuevo nodo.
     * Nuevo->Dato accede al campo Dato del nodo recien creado.
     */
    scanf("%d", &Nuevo->Dato);

    Nuevo->Sig = NULL; // Siempre apunta a NULL al crearse.

    /* Si la lista esta vacia, el nuevo nodo pasa a ser el primero. */
    if (!*Principio)
    {
        *Principio = Nuevo;

        return;
    }

    /* Recorre hasta el ultimo nodo el que tiene Sig == NULL */
    while(Actual->Sig)
        Actual = Actual->Sig;

    /* Enlaza el nuevo al final. */
    Actual->Sig = Nuevo;
}

/* Recorre e imprime la lista completa, desde el inicio hasta NULL. */
void Lee(struct Lis **Principio)
{
    /* *Actual empieza apuntando al primer nodo */
    struct Lis *Actual = *Principio;
    
    system("cls"); // Limpia la pantalla.

    /* Mientras Actual NO sea NULL, significa que aun hay nodos por recorrer. */
    while (Actual)
    {
        printf("\nEl dato es: %d", Actual->Dato);
        Actual = Actual->Sig; // Avanza al siguiente nodo.
    }

    printf("\n\nPresione una tecla para continuar\n");
    getch(); // Espera que se presione una tecla. 
}

/* Borra un nodo por valor */
void Borra(struct Lis **Principio)
{
    /* *Actual comienza apuntando al primer nodo, se usa para recorrer la lista.
     * *Anterior comienza apuntando al primer nodo, guarda el nodo previo, necesario para reconectar enlaces.
     */ 
    struct Lis *Actual = *Principio, *Anterior = *Principio;
    int b;
    
    system("cls"); // Limpia la pantalla.

    printf("\nIngrese el dato a borrar: ");
    scanf("%d", &b);

    /* Busca el nodo que contenga el valor */
    while ((Actual) && (b != Actual->Dato))
    {
        Anterior = Actual;
        Actual = Actual->Sig;
    }

    /* Caso 1: es el primer nodo */
    if ( (Actual) && (*Principio == Actual))
    {
        /* Se mueve el inicio al segundo nodo. */
        *Principio = Actual->Sig;
        free(Actual); // Se libera la memoria del nodo eliminado.

        return;
    }

    /* Caso 2: esta en medio o final */
    if (Actual)
    {
        /* El nodo Anterior ahora apunta al siguiente del nodo eliminado. */
        Anterior->Sig = Actual->Sig;
        free(Actual);

        return;
    }

    /* Caso 3: no encontrado */
    system("cls"); // Limpia la pantalla

    printf("\nEl dato no ha sido encontrado");
    printf("\n\nPresiones una tecla para continuar\n");

    getch(); // Espera  que se presione una tecla
}

/* Libera completamente la memoria de todos los nodos de la lista */
void Elimina(struct Lis **Principio)
{
    struct Lis *Actual;

    /* Mientras la lista no este vacia */
    while (*Principio)
    {
        Actual = *Principio;            // Guarda el nodo actual.
        *Principio = (*Principio)->Sig; // Avanza el inicio al siguiente nodo.
        free(Actual);                   // Libera el nodo que estaba primero (Actual).
    }
    /* Al terminar el bucle:
     * *Principio == NULL
     * La lista quedo completamente vacia.
     */
}

Lista Simple 02

Makefile


CC = gcc
CFLAGS = -std=c11 -Wall -Wextra -pedantic

PROG = main
OBJ = main.o

$(PROG): $(OBJ)
	$(CC) $(OBJ) -o $(PROG)

$(OBJ): main.c
	$(CC) $(CFLAGS) -c main.c

.PHONY: clean
clean:
	del $(OBJ) $(PROG).exe

Código:


// gcc -std=c11 -Wall -Wextra -pedantic main.c -o main

#include <stdio.h>
#include <stdlib.h> // Se usa system(), exit(), malloc(), free()
#include <conio.h> // Se usa getch() y getche()
#include <ctype.h> // Se usa toupper()

/* Definicion del nodo de la lista */
struct Lis
{
    int Dato;
    struct Lis *Sig; // Puntero al siguiente nodo
};

/* Prototipos de funciones */
void Ingresa(struct Lis **);
void Lee(struct Lis **);
void Borra(struct Lis **);
void Busca(struct Lis **);

int main(void)
{
    int Opcion;

    /* Inicio apunta al primer nodo de la lista.
     * Si es NULL => lista vacia.
     */
    struct Lis *Inicio = NULL;

    /* Bucle infinito tipo menu */
    for (;;)
    {
        system("cls"); // Limpia la pantalla.

        printf("\n1 - Ingresa datos\n");
        printf("2 - Lee datos\n");
        printf("3 - Borra datos\n");
        printf("4 - Busca datos\n");
        printf("5 - Salir\n\n");
        printf("Ingrese una opcion (1 - 5): ");
        
        Opcion = getche(); // Lee el caracter y lo muestra, sin esperar Enter

        switch(Opcion)
        {
            case '1':
                Ingresa(&Inicio);
                break;

            case '2':
                Lee(&Inicio);
                break;

            case '3':
                Borra(&Inicio);
                break;

            case '4':
                Busca(&Inicio);
                break;

            case '5':
                exit(0); // Finaliza inmediatamente el programa
        }
    }
    return 0;
}

/* Inserta un nodo en forma ordenada de mayor a menor */
void Ingresa(struct Lis **Principio)
{
    /* *Actual   => Se usa para recorrer la lista.
     * *Nuevo    => Nodo recien creado.
     * *Anterior => Mantiene el nodo previo para poder enlazar.
     */
    struct Lis *Actual, *Nuevo, *Anterior;
    
    /* Reserva dinamica de memoria para el nuevo nodo.
     * Si malloc devuelve NULL => No hay memoria disponible
     */
    if (! (Nuevo = (struct Lis *)malloc(sizeof(struct Lis))))
    {
        printf("\n\nNo hay memoria disponible\n");
        printf("\n\nPresiones una tecla para continuar\n");
        getch(); // Espera que se presione una tecla.

        return;
    }

    /* Carga del dato en el nodo nuevo */
    printf("\n\nIngrrese el dato: ");
    scanf("%d", &Nuevo->Dato);

    /* Caso 1: Si la lista esta vacia.
     * El Nuevo nodo pasa a ser el primero.
     */
    if (!*Principio)
    {
        *Principio = Nuevo;
        Nuevo->Sig = NULL;

        return;
    }

    /* Inicializacion de punteros para recorrer */
    Anterior = *Principio;
    Actual = *Principio;

    /* Busca la posicion correcta para mantener la lista ordenada de mayor a menor.
     * Busca mientras:
     * - No llegue al final.
     * - Y el valor Actual sea mayor que el Nuevo.
     */
    while((Actual->Dato > Nuevo->Dato) && Actual) // Deberia ser while(Actual && (Actual->Dato > Nuevo->Dato)) Dato no existiria si no existe Actual!
    {
        Anterior = Actual;
        Actual = Actual->Sig;
    }

    /* Caso 2: Si se llego al final (Actual == NULL). Insertamos al final. */
    if (!Actual)
    {
        Anterior->Sig = Nuevo;
        Nuevo->Sig = NULL;

        return;
    }

    /* Caso 3: Si se inserta al principio.
     * Ocurre cuando el nuevo valor debe ir antes del primero.
     */
    if (Anterior == Actual)
    {
        *Principio = Nuevo;
        Nuevo->Sig = Anterior;

        return;
    }

    /* Caso4: Insercion intermedia
     * Se reconectan los enlaces:
     * Anterior => Nuevo => Actual
     */
    Anterior->Sig = Nuevo;
    Nuevo->Sig = Actual;
}

/* Recorre e imprime la lista */
void Lee(struct Lis **Principio)
{
    /* *Actual apunta a el primer nodo */
    struct Lis *Actual = *Principio;

    /* Recorre hasta que Actual sea NULL, desde inicio a NULL */
    while (Actual)
    {
        printf("\nEl dato es: %d\n", Actual->Dato);
        Actual = Actual->Sig;
    }

    printf("\n\nPresione una tecla para continuar\n");
    getch(); // Espera que se presione una tecla.
}

/* Elimina un nodo si existe, con pedido de confirmacion de la eliminacion */
void Borra(struct Lis **Principio)
{
    struct Lis *Actual, *Anterior;
    int Valor;
    char Opcion;

    /* Carga del dato a eliminar */
    printf("\nIngrese el dato a borrar: ");
    scanf("%d", &Valor);
    
    Actual = *Principio;
    Anterior = *Principio;

    /* Busca el nodo */
    while ((Valor != Actual->Dato) && Actual) // Lo mismo la condicion && esta invertida, Si Actual no existe, Dato no existira!
    {
        Anterior = Actual;
        Actual = Actual->Sig;
    }

    /* Si fue encontrado */
    if (Actual)
    {
        printf("\nEl dato es: %d\n\n", Actual->Dato);
        printf("\n\nDesea eliminar este dato (S / N)");

        Opcion = toupper(getche()); // Cambia a mayuscula lo ingresado y lo muestra y almacena en la variable

        if (Opcion == 'S')
        {
            /* Caso 1: Si es el primer nodo */
            if (Anterior == Actual)
            {
                *Principio = Actual->Sig; // El segundo nodo pasa a ser el primero
                free(Actual); // Libera la memoria del nodo eliminado.

                return;
            }
            
            /* Caso 2: Nodo intermedio o final */
            Anterior->Sig = Actual->Sig; // Se saltea el nodo a eliminar
            free(Actual); // Libera la memoria del nodo eliminado

            return;
        }

        return;
    }

    /* Si no se encontro */
    printf("\nEl dato no ha sido encontrado");
    printf("\n\nPresione una tecla para continuar\n");

    getch(); // Espera que se presione una tecla.

}

/* Busca un nodo por valor */
void Busca(struct Lis **Principio)
{
    struct Lis *Actual;
    int Valor;

    printf("\nIngrese el dato a buscar: ");
    scanf("%d", &Valor);

    Actual = *Principio;

    /* Recorre la lista buscando coincidencia */
    while ((Valor != Actual->Dato) && Actual) // Nuevamente && con condiiones invertidas
    {
        Actual = Actual->Sig;
    }

    /* Si fue encontrado */
    if (Actual)
    {
        printf("\nEl dato es: %d\n\n", Actual->Dato);
        printf("\n\nPresione una tecla para continuar\n");

        getch(); // Espera que se presione una tecla.

        return;
    }

    /* Si no fue encontrado */
    printf("\nEl dato no ha sido encontrado");
    printf("\n\nPresione una tecla para continuar\n");

    getch(); // Espera que se presione una tecla.
    
}

Lista Simple 03

Makefile


CC = gcc
CFLAGS = -std=c11 -Wall -Wextra -pedantic

PROG = main
OBJ = main.o

$(PROG): $(OBJ)
	$(CC) $(OBJ) -o $(PROG)

$(OBJ): main.c
	$(CC) $(CFLAGS) -c main.c

.PHONY: clean
clean:
	del $(OBJ) $(PROG).exe

Código:


// gcc -std=c11 -Wall -Wextra -pedantic main.c -o main

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h> // Se usa toupper()

/* Definicion de nodo de lista simplemente enlazada */
struct Lis
{
  int Dato; /* Valor almacenado en el nodo */
  struct Lis *Sig; /* Puntero al siguiente nodo */
};

/* Prototipos de funciones */
void Ingresa(struct Lis **);
void Lee(struct Lis **);
void Borra(struct Lis **);
void Busca(struct Lis **);
void Contar(struct Lis **);

int main(void)
{
  int Opcion;

  /* Inicializa la lista vacia */
  struct Lis *Inicio = NULL;

  /* Bucle infinito (menu) */
  for(;;)
  {
    system("cls"); /* Limpia la pantalla en Windows, en Linux "clear" */

    /* Menu de opciones */
    printf("1 - Ingresa datos\n");
    printf("2 - Lee datos\n");
    printf("3 - Borrar datos\n");
    printf("4 - Buscar datos\n");
    printf("5 - Contar nodos\n");
    printf("6 - Salir\n\n");
    printf("Ingrese una opcion ( 1 - 5 ) : ");

    /* Lee opcion como caracter */
    Opcion = getchar();

    /* Limpia buffer no estandar */
    fflush(stdin);

    switch(Opcion)
    {
    case '1':
      Ingresa(&Inicio);
      break;
    case '2':
      Lee(&Inicio);
      break;
    case '3':
      Borra(&Inicio);
      break;
    case '4':
      Busca(&Inicio);
      break;
    case '5':
      Contar(&Inicio);
      break;
    case '6':
      exit(0); // Finaliza programa
    }
  }
  return 0;
}

/* Inserta un nodo en la lista de forma ordenada */
void Ingresa(struct Lis **Principio)
{
  struct Lis *Actual, *Nuevo, *Anterior;

  /* Reserva memoria para un nuevo nodo */
  if (!(Nuevo = (struct Lis *)malloc(sizeof(struct Lis))))
  {
    printf("\n\nNo hay memoria disponible\n");
    system("pause");
    return;
  }

  /* Carga el dato en el nuevo nodo */
  printf("\n\nIngrese el dato : ");
  scanf("%d", &Nuevo->Dato);
  fflush(stdin);

  /* Caso lista vacia */
  if (!*Principio)
  {
    *Principio = Nuevo;
    Nuevo->Sig = NULL;
    return;
  }

  /* Recorre la lista para ubicar posicion ordenada */
  Anterior = *Principio;
  Actual = *Principio;

  while (Actual && (Actual->Dato < Nuevo->Dato))
  {
    Anterior = Actual;
    Actual = Actual->Sig;
  }

  /* Insertar al final */
  if (!Actual)
  {
    Anterior->Sig = Nuevo;
    Nuevo->Sig = NULL;
    return;
  }

  /* Insertar al inicio */
  if (Anterior == Actual)
  {
    *Principio = Nuevo;
    Nuevo->Sig = Anterior;
    return;
  }

  /* Insertar en el medio */
  Anterior->Sig = Nuevo;
  Nuevo->Sig = Actual;
}

/* Recorre y muestra todos los nodos */
void Lee(struct Lis **Principio)
{
  struct Lis *Actual = *Principio;
  while (Actual)
  {
    printf("\nEl dato es : %d\n", Actual->Dato);
    Actual = Actual->Sig;
  }
  system("pause");
}

/* Busca y elimina un nodo por valor */
void Borra(struct Lis **Principio)
{
  struct Lis *Actual, *Anterior;
  int Valor;
  char Opcion;

  printf("\nIngrese el dato a borrar : ");
  scanf("%d", &Valor);
  fflush(stdin);

  Actual = *Principio;
  Anterior = *Principio;

  /* Busca el nodo */
  while (Actual && (Valor != Actual->Dato))
  {
    Anterior = Actual;
    Actual = Actual->Sig;
  }
  /* Si lo encontro */
  if (Actual)
  {
    printf("\nEl dato es : %d\n\n", Actual->Dato);
    printf("\n\nDesea eliminar este dato (S / N) ");

    Opcion = toupper(getchar());
    fflush(stdin);
    
    if (Opcion == 'S')
    {
      /* Si es el primer nodo */
      if (Anterior == Actual)
      {
        *Principio = Actual->Sig;
        free(Actual);
        return;
      }

      /* Nodo intermedio o final */
      Anterior->Sig = Actual->Sig;
      free(Actual);
      return;
    }
    return;
  }

  printf("\nEl dato no ha sido encontrado\n\n");
  system("pause");
}

/* Busca un nodo y muestra su direccion */
void Busca(struct Lis **Principio)
{
  struct Lis *Actual;
  int Valor;

  printf("\nIngrese el dato a buscar : ");
  scanf("%d", &Valor);
  
  Actual = *Principio;
  
  /* Recorre hasta encontrar */
  while (Actual && (Valor != Actual->Dato))
    Actual = Actual->Sig;
  
  if (Actual)
  {
    printf("\nEl dato es : %d\tSu ubicacion es : %p\n\n", Actual->Dato, Actual); // Castear a (void *)Actual
    
    system("pause");
    return;
  }

  printf("\nEl dato no ha sido encontrado\n\n");
  system("pause");
}

/* Cuenta la cantidad de nodos de la lista */
void Contar(struct Lis **Principio)
{
  int Contador = 0;
  struct Lis *Actual;
  Actual = *Principio;

  /* Recorre toda la lista */
  while (Actual)
  {
    Contador++;
    Actual = Actual->Sig;
  }
  
  printf("\n\nLa cantidad de nodos de la lista es : %d\n\n", Contador);
  system("pause");
}