El objetivo principal de este proyecto es desarrollar un programa llamado push_swap que reciba una lista de números enteros como argumentos y genere la secuencia más corta de instrucciones para ordenar esos números en orden ascendente. Este ejercicio no solo refuerza conceptos fundamentales sobre algoritmos de ordenamiento, sino que también introduce al desarrollador a la complejidad algorítmica.
El programa recibe como argumento una lista de números enteros que se cargará en el stack a. Un paso previo a la carga de números consite en procesar la entrada de datos numéricos y asegurarse de que sean válidos para su ordenación. A partir de aquí, el objetivo es ordenarlos de menor a mayor utilizando las siguientes operaciones:
| Instrucción | Descripción |
|---|---|
| sa | Intercambia los dos primeros elementos de la stack a |
| sb | Intercambia los dos primeros elementos de la stack b |
| ss | Intercambia los dos primeros elementos de a y b a la vez |
| pa | Toma el primer elemento de b y lo coloca en a |
| pb | Toma el primer elemento de a y lo coloca en b |
| ra | Rota todos los elementos de a hacia arriba |
| rb | Rota todos los elementos de b hacia arriba |
| rr | Rota a y b hacia arriba simultáneamente |
| rra | Rota todos los elementos de a hacia abajo |
| rrb | Rota todos los elementos de b hacia abajo |
| rrr | Rota a y b hacia abajo simultáneamente |
El programa imprime en salida estándar la secuencia de operaciones necesaria para ordenar la lista.
Para la implementación del proyecto, se utilizó la estructura original de listas enlazadas desarrollada en el proyecto libft. Se creó un nuevo struct llamado t_content, que almacena un valor entero a ordenar y otro entero que indica la posición en la que debería estar dicho número una vez que la lista esté ordenada:
typedef struct s_content
{
int value; // Valor numérico
int sorted_pos; // Posición ordenada del valor
} t_content;Este tipo de dato será el que maneje la lista enlazada, de tal forma que planteado con este enfoque se simplifica el manejo de datos y permite reducir complejidad y el número de funciones implementadas y una gestión eviciente de los valores y facilita la indexación sin recorrer los stacks repetidamente (K.I.S.S).
Se han planteado varios algoritmos de forma escalonada en función de la cantidad de numeros a ordenar:
Ordena una stack compuesta de 3 números aplicando soluciones triviales sin tener que usar la stack b
Ordena una stack compuesta de numeros comprendidos entre 4 y 7 haciendo hasta tres bucles de ordenación iterativa pasando los números más grandes a la stack b y cuando solo queden 3 números en la stack a se reutiliza el algoritmo anterior y se introducen los números que se pasaron a la stack b ya ordenados
Ordena una stack de números mayores a 7. Para lograr la ordenación en el menor número de movimientos, el algoritmo k-sort optimiza el proceso agrupando elementos en subconjuntos y realizando una ordenación incremental. Este enfoque permite reducir significativamente el número de operaciones en comparación con algoritmos más simples.
Según el propósito y utilidad de cada función se han organizado en diferentes archivos:
-
ps_input_utils.c: Lectura y validación de argumentos numéricos de tipo int no duplicados y carga de estos en la stack a
load_stack(int argc, char **argv): Carga los números desde los argumentos en la stack a si los datos son válidos, si no devuelve null.
-
ps_array_utils.c: Manejo de arrays
- Funciones para crear y manejar arrays que serán útiles para elaborar un índice con la posición de cada elemento una vez sea ordenado. Para ello se crea un array que contenga los mismos números de la stack a manteniendo su orden, se creará un duplicado de ese array con los elementos ya ordenados utilizando el algoritmo de la burbuja y finalmente se creará un índice buscando para cada número sin ordenar en el array ordenado para determinar cual es su posición estando ordenado para luego cargar dichos valores en la stack a y liberar la memoria de los arrays.
-
ps_swap.c: Intercambia los dos primeros elementos de una stack
swap_a(t_list **stack_a): Intercambia los dos primeros elementos del stacka.swap_b(t_list **stack_b): Intercambia los dos primeros elementos del stackb.swap_a_b(t_list **stack_a, t_list **stack_b): Realizaswap_ayswap_bsimultáneamente.
-
ps_push.c: Movimiento entre stacks
push_a(t_list **stack_b, t_list **stack_a): Mueve el primer elemento del stackbal stacka.push_b(t_list **stack_a, t_list **stack_b): Mueve el primer elemento del stackaal stackb.
-
ps_rotate.c: Rotación
rotate_a(t_list **stack_a): Rota hacia arriba todos los elementos del stacka.rotate_b(t_list **stack_b): Rota hacia arriba todos los elementos del stackb.rotate_a_b(t_list **stack_a, t_list **stack_b): Realiza ambas rotaciones simultáneamente.
-
ps_rev_rotate.c: Rotación inversa
rev_rotate_a(t_list **stack_a): Rota hacia abajo todos los elementos del stacka.rev_rotate_b(t_list **stack_b): Rota hacia abajo todos los elementos del stackb.rev_rotate_a_b(t_list **stack_a, t_list **stack_b): Realiza ambas rotaciones inversas simultáneamente.
-
ps_stack_utils.c: Utilidades para stacks
- Funciones para obtener posiciones mínimas y máximas, calcular costos de rotación y verificar si un stack está ordenado.
-
ps_sort.c: algoritmos de ordenación
three_sort(t_list **stack): Ordena una lista de tres elementosmini_sort(t_list **stack_a, t_list **stack_b, int size): Ordena una lista de cuatro a siete elementosk_sort(t_list **stack_a, t_list **stack_b, int size): Ordena una lista mayor de 7 números
Para aprobar el proyecto se deben cumplir los siguientes criterios de eficiencia:
- Ordenar 100 números en menos de 700 operaciones. ✅
- Ordenar 500 números en menos de 5500 operaciones. ✅
Una vez completada la parte obligatoria, se implementó un programa adicional llamado checker. Este programa replica el funcionamiento del proyecto principal pero en lugar de aplicar un algoritmo de ordenación, ejecuta una a una las instrucciones leídas desde la entrada estándar. Para ello se utiliza la función get_next_line, implementada en un proyecto anterior. Al finalizar las instrucciones, se verifica si el stack está correctamente ordenado y devuelve "OK", "KO" o "Error" según corresponda.
