Push_swap
Push_swap
Push_swap is a highly efficient C project focused on sorting data on a stack, with a limited set of instructions, using the lowest possible number of actions. It requires manipulating various types of algorithms and choosing the most appropriate solution for an optimized data sorting.
Project Requirements
- Goal: Sort stack
ain ascending order using stackbas a helper. - Operations:
sa,sb,ss,pa,pb,ra,rb,rr,rra,rrb,rrr. - Error Handling: Must handle non-numeric parameters, duplicates, and integer overflows/underflows (INT_MIN/MAX).
- Performance: Optimized to sort 100 numbers in fewer than 700 moves and 500 numbers in fewer than 5500 moves (standard 42 benchmarks).
- Memory: No memory leaks; all stack nodes must be freed upon exit.
The Algorithm Strategy
My implementation uses a hybrid approach to ensure maximum efficiency depending on the stack size:
1. Coordinate Compression (Indexing)
Before sorting, the program replaces each number with its relative rank (0 to N-1). This “Indexing” step allows the sorting algorithm to handle negative numbers and very large values identically.
void indexing(t_node **stack)
{
t_node *curr_node;
int index;
index = 0;
curr_node = get_min(stack);
while (curr_node)
{
curr_node->index = index++;
curr_node = get_min(stack);
}
}
2. Small Sets: Hard Sort
For stacks of 5 numbers or fewer, I implemented a “Hard Sort” which uses specific, hardcoded logic to find the optimal move sequence for every possible permutation.
3. Large Sets: Bitwise Radix Sort
For larger datasets, I implemented a Radix Sort algorithm. By looking at the binary representation of the indices, the algorithm pushes numbers to stack b based on whether a specific bit is 0 or 1, then pushes them back to a. This results in a very predictable and efficient O(n * log(n)) complexity.
void radin_sort(t_node **stack_a, t_node **stack_b)
{
int i;
int j;
int size;
int max_bit;
size = stack_size(*stack_a);
max_bit = max_bits(stack_a);
i = 0;
while (i < max_bit)
{
j = 0;
while (j++ < size)
{
if ((((*stack_a)->index >> i) & 1) == 1)
ra(stack_a);
else
pb(stack_a, stack_b);
}
while (stack_size(*stack_b) != 0)
pa(stack_a, stack_b);
i++;
}
}
Error Handling & Parsing
The parser ensures that every input is a valid integer. It checks for:
- Duplicates: No two numbers can be the same.
- Overflows: Numbers must fit within a 32-bit
int. - Invalid Characters: Only digits and signs (+/-) are permitted.
static int valid_arg(char *str)
{
int i;
i = 0;
if (str[i] == '-' || str[i] == '+')
i++;
if (!str[i])
return (0);
while (str[i])
{
if (str[i] < '0' || str[i] > '9')
return (0);
i++;
}
return (1);
}
Usage
1. Build
make
2. Run
./push_swap 2 1 3 6 5 8
3. Visualizer (Optional)
To see the algorithm in action, you can pipe the output to a visualizer:
ARG="4 67 3 87 23"; ./push_swap $ARG | ./checker_linux $ARG
Skills Learned
- Algorithm Complexity: Analyzing time (O) and space complexity.
- Data Structures: Implementing and manipulating doubly linked lists.
- Bitwise Logic: Using bit-shifting and masking for advanced sorting.
- Testing: Unit testing against thousands of random permutations to ensure stability.
Author
- GitHub: cuteKittenArri